perm filename CNVR.MAN[UP,DOC] blob sn#084113 filedate 1974-01-24 generic text, type T, neo UTF8























                                 ␈↓↓Son␈↓ ␈↓↓of␈↓ ␈↓↓Conniver␈↓

                          ␈↓↓The␈↓ ␈↓↓Conniver␈↓ ␈↓↓Reference␈↓ ␈↓↓Manual␈↓

                                   Version II
                                       by
                                Drew V. McDermott
                                       and
                               Gerald Jay Sussman




























                                                                VII.3.3 2





                                     ␈↓↓Apology␈↓

            This manual is intended to be a guide to the philosophy and
        use of the programming language Conniver, which is "complete,"
        and running at the AI Lab now.  It assumes good knowledge of
        Lisp, but ␈↓↓no␈↓ knowledge of Micro-Planner, in whose implementation
        many design decisions were made that are not to be expected to
        have consequences in Conniver.  Those not familiar with Lisp
        should consult Weissman's (1967) ␈↓↓Primer␈↓, the ␈↓↓Lisp␈↓ ␈↓↓1.5␈↓
        ␈↓↓Programmer␈↓'␈↓↓s␈↓ ␈↓↓Manual␈↓ (McCarthy ␈↓↓et␈↓. ␈↓↓al␈↓., 1962), or Jon L. White's
        (1970) and others' (PDP-6, 1967) excellent memos here at our own
        lab.   
             Conniver embodies few original ideas, but is hopefully an
        original combination of the good ideas of others.  We must
        acknowledge Carl Hewitt's Planner language (Hewitt, 1971) for
        giving us most of our ideas about data structure, although
        Conniver looks at its world differently from Planner.  The
        control structure, including the concepts "access" and "control,"
        was enormously influenced by Daniel G. Bobrow.  (Bobrow and
        Wegbreit, 1972).  The variable declaration syntax is closely
        related to the MUDDLE syntax developed by Christopher Reeve.  Our
        philosophy has been greatly influenced by Joel Moses.  
            Several people read the first versions of this manual and
        influenced this one, especially David McDonald, Terry Winograd,
        Sidney Markowitz, Michael Speciner, and Jeff Rubin.  The current
        semantics of data-property functions is partly due to a
        suggestion by Michael Genesreth.  Last in this category, but not
        least in one respect, is Michael Levin; his confusion at the
        terseness of some of my explanations has gone one step toward
        avenging the confusion of an entire generation of programmers at
        his Lisp 1.5 manual.  
                Some of the notational conventions of the manual are:  
              Actual code is in upper case 
              Syntactic variables are lower case 
              Optional arguments or list components are delimited by
                brackets ([,]) surrounding the syntactic variable and its
                default value, if any.  
              Segment syntactic variables are delimited by stars (*).  
              Quoted arguments are flagged with a quote (').  Unevaluated
                segment arguments start with '*.  
              "Control-letter" is indicated by "}letter".  "Up-arrow" is
                denoted by ↑; "Left arrow," by   .  
              "Altmode" is denoted by $.  
              Arguments are often given mnemonic names, as in (ADD atom).
                If the argument is not quoted, this notation means it is
                to evaluate to something described by the name; hence,
                (ADD (CAR X)) is legal if X starts with an atom.  
                                                        DVM 





                                                                VII.3.3 3





                                    ␈↓↓Contents␈↓

        I BASIC CONNIVER
          1 Introduction
          2 Pattern-Accessible Data
          3 Conniver Programs
          4 Contexts
          5 Defining Functions
          6 Generators
          7 Generalized Control Structures


        II HAIRY CONTROL STRUCTURE

          1 What a Frame Is
           1.1 How to Be a Programming Language
           1.2 Conniver Control Structure
            1.2.1 Frames
            1.2.2 Tags
            1.2.3 Closures

          2 Methods
           2.1 If-addeds and If-removeds
           2.2 If-neededs
           2.3 Closures of Methods

          3 Generators

          4 Applications
           4.1 Backtracking
           4.2 Multiprocessing


        III HAIRY DATA STRUCTURE

          1 Types of Data
           1.1 Objects
           1.2 Indexable Data
            1.2.1 Types of Indexable Data
             1.2.1.1 Item Data
             1.2.1.2 Methods and Method Closures

            1.2.2 The Index

          2 Properties of Data

          3 Implementation of Contexts
           3.1 Presence and Absence
           3.2 Implementation of Properties
           3.3 Context Layers



                                                                VII.3.3 4





           3.4 Nonstandard Contexts

          4 Named Data


        IV ON PATTERN-DIRECTED INVOCATION


        V USING CONNIVER


        VI LISP AND CONNIVER


        VII APPENDIX:  THE CONNIVER REFERENCE SOURCE

          1 The Evaluator
           1.1 Communication with Lisp
             A. RUN, STOP
             B. START

           1.2 Procedure Definition
             A. CDEFUN
             B. IF-ADDED, IF-REMOVED, IF-NEEDED

           1.3 Sequence Evaluators
             A. PROG, PROGBIND
             B. COND
             C. AND, OR

           1.4 Frame Creators and Manipulators
             A. FRAME, ACCESS, CONTROL, EXPRESSION
             B. TAG, ACTBLOCK
             C. VFRAME
             D. CLOSURE
             E. SETACCESS, SETCONTROL
             F. SAMEFRAME

           1.5 Alteration of Flow of Control
             A. CONTINUE, GO
             B. EXIT, RETURN, DISMISS
             C. ADIEU, AU-REVOIR

           1.6 Relative Evaluation
             A. CEVAL
             B. CALL
             C. INVOKE

           1.7 Variable Manipulators
             A. VLOC, RVALUE, /,, LVALUE, ASSIGNED?



                                                                VII.3.3 5





             B. CSET, CSETQ, UNASSIGN

           1.8 Possibilities Lists
             A. TRY-NEXT
                Possibilities types:  *METHOD, *GENERATOR,*AU-REVOIR,
                  *NOTE, user-defined types, values.
             B. GENERATE
             C. INSTANCE
             D. NOTE
             E. ADIEU, AU-REVOIR
             F. GET-POSSIBILITIES
             G. SET-POSSIBILITIES

           1.9 The Interrupt System
             A. CINTERRUPT, NOW
             B. ALLOW


          2 The Data Base
           2.1 Data-Base Initialization
             DATA-INIT

           2.2 Datum Creation and Manipulation
             A. OBJECT
             B. NAME-DATUM
             C. DATUM
             D. ITEM
             E. IF-ADDED, IF-REMOVED, IF-NEEDED
             F. METHOD-TYPE, DELETE-METHOD-TYPE

           2.3 Enlarging, Depleting, and Searching the Data Base
             A. REALIZE, UNREALIZE, ACTUALIZE, UNACTUALIZE
                ADD, REMOVE, INSERT, KILL
             B. ADD, REMOVE, INSERT, KILL
             C. FETCH, FETCHI, FETCHM

           2.4 Properties of Data
             A. REAL, UNREAL, PRESENT, ABSENT
             B. DPUT, DGET, DREM, DPUT+, DGET+, DREM+
             C. DPUTL, DGETL, DREML

           2.5 Manipulating Contexts
             A. LAYER, FLUSH
             B. PUSH-CONTEXT, POP-CONTEXT, FINALIZE,
                NEW-CONTEXT, SPLICE
             C. IN-CONTEXT
             D. MENTIONERS, CONTEXT+
             E. C-MARKER





                                                                VII.3.3 6





           2.6 The Matcher
             A. !>var
             B. !,var
             C. !,(var value)
             D. !<var
             E. !?var
             F. !;var
             G. !'var


          3 Debugging in Conniver
           3.1 Listen-Loop Builders and Manipulators
             A. LISTEN, EAR, NEAR, TOP
             B. UP, DOWN, J
             C. CERR, CBREAK, CERRMESS

           3.2 Data Printers
            A. CPRINT, CPRIN1
            B. EXPRESSION, BACKTRACE
            C. PATH

           3.3 Tracing in Conniver
             A. CTRACE
             B. CUNTRACE
             C. CDISPLAY
             D. /:


        Bibliography

























                                                                      I 7





        I BASIC CONNIVER 



        I.1 Introduction 



            Conniver is a programming language designed to make easy the

        definition of processes cooperating to solve problems in the

        realm of Artificial Intelligence.  These problems are often

        characterized by unpredictability of data-structure formats and

        flow of control, since programmers must try to use them to model

        flexibly some ill-defined part of the world.  Each programmer

        wishes to make additions or changes to the data of his model in a

        way that is as close as possible to what he is thinking, without

        having to translate into some internal representation.  It is

        just as true that he cannot be bothered with representations of

        processes and procedures; he would like to be able to refer to

        environments he perceives as "here" or "where control was a

        moment ago," without regard to where these environments exist on

        some internal stack, or whether they can even be referred to

        without advance preparation.  This second aspect of A.I. problems

        is especially prominent when procedures are regarded as data

        ("beliefs"), to be monitored and understood as well as executed.

            Conniver is a Lisp-like language which clears up some of

        these deficiencies in Lisp with two additions:  

            (1) a system-maintained data base 

            (2) the ability to manipulate arbitrary control environments.




                                                                    I.1 8





            Neither of these features is new.  Readers who are acquainted

        with predicate calculus or PLANNER will soon recognize the more

        obvious characteristics of the data base.  Those with knowledge

        of PAL and to some extent the lambda-calculus and Lisp 1.5 will

        already understand Conniver's control structure.  





        I.2 Pattern-Accessible Data 



            The data base is the place to begin.  In some computer

        applications, "data" means huge arrays of numbers, which it is

        desired to store efficiently and crunch quickly; or a group of

        strings stored in well-defined variables, to be manipulated in a

        well-defined sequence.  In A.I., the process that creates a datum

        does not usually know who wants it or what he wants it for.  It

        cannot, therefore, store it in a standard variable.  Furthermore,

        data are often not numbers but ␈↓↓facts␈↓, such as "Mary is the mother

        of God."  These facts are usually not so much computed as

        discovered, and they are not usually passed on to the next stage

        of the computation, but merely "made available" to whomever needs

        them.  For example, some process may later ask, "Who is the

        mother of God?" or "Who are the children of Mary?" and the fact

        given must be accessible.  

            This is achieved by letting facts be modelled as arbitrary

        non-circular list structures, which are accessible via any




                                                                   I.2 9





        combination of their components.  These data structures are

        called ␈↓↓items␈↓, and they are just "there" in the data base, rather

        than being thought of as properties pointed to by their

        individual atoms.  Thus, a process may execute 

                (ADD '(MARY MOTHER-OF GOD)) 

        and that item will be ␈↓↓present␈↓ to other processes that need it.

        (Notice that Conniver syntax is that of M.I.T. Lisp.  The given

        ␈↓↓form␈↓ calls for the application of the function ADD to one quoted

        (unevaluated) argument, namely (MARY MOTHER-OF GOD).)  The effect

        of ADD can be undone with REMOVE, as in 

                (REMOVE '(MARY MOTHER-OF GOD)) 

        which leaves the data base in the state it was in before the ADD.

            After an ADD, if another process wishes to access a fact, it

        may do so in several ways.  All of them rest on the notion of

        specifying part of a desired fact and letting the system find all

        present items which agree in the specified part.  This is a form

        of associative memory in which the user is free to let any piece

        of an item be its key, and all the rest to be what is retrieved.

        The specified and unspecified parts are intermixed in a ␈↓↓pattern␈↓

        which resembles the item, but has ␈↓↓variables␈↓ instead of ␈↓↓constants␈↓

        in the parts to be filled in by the memory system.  For example,

        the pattern (!>WHO MOTHER-OF GOD) specifies "mother" and "God,"

        but has a slot labeled WHO to be filled in (i.e., be assigned as

        a variable) if there is an item (someone MOTHER-OF GOD) present

        in the data base.  The characters "!>" specify that WHO is a




                                                                   I.2 10





        variable, i.e., the name of the contents of the slot, not the

        actual contents.  

            The most fundamental way to use these patterns is with the

        function FETCH, which takes a pattern as an argument, and returns

        a ␈↓↓possibilities␈↓ ␈↓↓list␈↓ containing all items which ␈↓↓match␈↓ it in the

        sense described.  In my example, (FETCH '(!>WHO MOTHER-OF GOD))

        returns 

        ((*POSSIBILITIES (!>WHO MOTHER-OF GOD)) 
         *IGNORE 
         (*ITEM ((MARY MOTHER-OF GOD) (0 (0 . +))) 
                ((WHO MARY)))).  


        This list contains all the items (here there is only one) which

        match the pattern, and a good deal more besides.  (FETCH returns

        a lot of information which it must compute anyway; usually most

        of it is discarded.)  The detailed format of this list will be

        described later.  
























                                                                   I.3 11





        I.3 Conniver Programs 



            Usually, a list of items such as this will be processed by

        looping through its elements, looking for one with some desired

        property or doing something to each of them.  This is done by

        using the function TRY-NEXT, which removes the next possibility

        from the list and returns it.  In the case of item possibilities,

        it returns the item and sets the pattern variables as the

        association list in its item possibility directs.  If the

        variable P is bound to the list given, (TRY-NEXT P) has the value

        ((MARY MOTHER-OF GOD) (0 (0 . +))), and the side effect of

        setting variables as the association list ((WHO MARY)) dictates;

        i.e., it gives the variable WHO the value MARY.  (The markers, of

        the form (0 (0 . +)), on the item returned may be ignored for

        now.) 

            Now imagine the items (MARY MOTHER-OF GOD), (RHEA MOTHER-OF

        GOD), and (ISIS MOTHER-OF GOD) are in the data base.  (The idea

        that a creature can have only one mother is, of course, not built

        into the system.)  The following program will print out all these

        mothers:  



        (PROG "AUX" (WHO (P (FETCH '(!>WHO MOTHER-OF GOD)))) 
        :LOOP (TRY-NEXT P '(RETURN NIL)) 
              (PRINT WHO) 
              (GO 'LOOP)   ) 







                                                                   I.3 12





        This program  is reminiscent of a Lisp PROG, with some

        differences:   

            (1) Local variables are declared as auxiliaries explicitly by

        putting the atom "AUX" before the bound variable list for the

        PROG.  (The list and marker could be omitted entirely.)  These

        variables are not automatically bound to NIL.  Atoms appearing in

        the list (like WHO) are bound but unassigned.  List of the form

        (atom expression), such as (P (FETCH...)), specify that atom is

        to be bound to the value of expression.  

            (2) The second argument to TRY-NEXT gives its value when

        there are no more possibilities.  (It must be quoted to avoid

        being evaluated when passed to TRY-NEXT.) 

            (3) Tags are preceded by ":" to distinguish them from

        ordinary atoms appearing in PROG bodies.  This is necessary

        because Conniver PROG's return the value of the last expression

        in their bodies.  For example, 



        (PROG "AUX" (WHO (P (FETCH '(!>WHO MOTHER-OF GOD)))) 
           (TRY-NEXT P '(RETURN NIL)) 
           WHO) 


        returns the first mother of God it finds, or NIL if there aren't

        any.   

            (4) GO always evaluates its argument.  










                                                                   I.4 13





        I.4 Contexts 



            So far, I have spoken in terms of one data base or world

        model, in which items can be added and fetched.  The major novel

        feature of the Conniver data base is that any number of world

        models, called ␈↓↓contexts␈↓, are allowed.  Each may be manipulated

        entirely independently.  These different states may be used to

        model hypothetical worlds, alternative cases of a proof,

        different board positions in a game, or different times, or all

        of these.  

            The user starts with a single, global context, bound to the

        variable CONTEXT.  He may change it by executing ADD to store a

        new item, or REMOVE to delete an old one.  These changes will

        cause items actually to appear or disappear from the "index"

        which implements the associative data base.  (See Chapter III.)

        A rather different kind of change is achieved if the form 

                (CSETQ CON1 (PUSH-CONTEXT CONTEXT)) 

        is evaluated, which stores a new context as the value of variable

        CON1.  (CSETQ is the Conniver analogue of Lisp's SETQ.)  The

        result is a new data base whose contents are initially exactly

        the same as those of CONTEXT.  CON1 is said to be a ␈↓↓sub-context␈↓

        of CONTEXT; this is meant purely in the sense that a stack frame

        of a language processor is a sub-frame of the frame beneath it on

        the stack.  (See Chapter II.  Contexts are implemented as stacks

        of "layers," which are analogous to stacks of frames.)  Although




                                                                   I.4 14





        initially CON1 is identical to CONTEXT, arbitrary additions and

        removals may happen to it which have no effect at all on CONTEXT.

            For example, assume that the "theology problem solver" I have

        been describing has in its data base (CONTEXT) the item (MARY

        MOTHER-OF GOD) (and that the other mothers have been removed).  A

        routine might perform the following actions, which involve the

        specification of a new religion (or a heresy):  

                (CSETQ CON1 (PUSH-CONTEXT CONTEXT)) 
                (REMOVE '(MARY MOTHER-OF GOD) CON1) 
                (ADD '(KONNIVA MOTHER-OF GOD) CON1) 


        Now CON1 differs from CONTEXT only in whom it represents as the

        mother of God.  Notice that ADD, REMOVE, and FETCH take optional

        second arguments which specify which context they apply to.  The

        default value of this argument is the value of CONTEXT.  Now

        (FETCH '(!>WHO MOTHER-OF GOD)) has the same effect as before, but

        (FETCH '(!>WHO MOTHER-OF GOD) CON1) returns 

        ((*POSSIBILITIES (!>WHO MOTHER-OF GOD)) 
         *IGNORE 
         (*ITEM ((KONNIVA MOTHER-OF GOD) (10 (0 . +))) 
                ((WHO KONNIVA)))).  


            I shall work into a less sacrilegious example, which will

        serve as a justification for Conniver control structure, by

        making the following observation.  A common technique in

        Conniving is to ␈↓↓rebind␈↓ CONTEXT (e.g., in an "AUX" list) to a sub-

        context.  This has the effect of making "hypothetical" all data-

        base changes performed inside the scope of the new binding.  It

        also means that returning from this scope causes all these




                                                                   I.5 15





        changes to disappear, unless special precautions are taken.  





        I.5 Defining Conniver Functions 



            The usual place besides PROG's in which one statement after

        another is evaluated is inside a function body.  

            I mentioned before that contexts may be used to model board

        positions in a game.  The example I will now pursue is a part of

        a tic-tac-toe program which has never been completed.  For this

        program, I organize the data base as a collection of items about

        the squares.  A square is a number from 1 to 9; a player is one

        of the symbols O or X.  In the initial context, there is an item

        of the form (FREE s) for all squares.  When a player p makes a

        move in square s, the item (HAS p s) replaces (FREE s).  

            One of the subroutines required in a tic-tac-toe program is

        (FORCEWIN player square), which is non-NIL if and only if a

        player can force a win on the next move of a game by playing in

        square.  It is defined using CDEFUN, which is analogous to Lisp's

        DEFUN:  

        (CDEFUN FORCEWIN (PLAYER SQUARE) 
              "AUX" ((CONTEXT (PUSH-CONTEXT CONTEXT)) 
                     (WM !"((*POSSIBILITIES) 
                            *IGNORE 
                            (*GENERATOR (WINMOVES PLAYER))))) 
           (ADD !"(HAS ,PLAYER ,SQUARE)) 
           (REMOVE !"(FREE ,SQUARE)) 
           (COND ((MAKEMOVE (OTHER PLAYER)) 
                  (TRY-NEXT WM NIL))   )).  





                                                                   I.5 16





        In English, square is a forced win for player if player still has

        a winning move after the other makes his best reply to square.

        Notice that this statement has a hypothetical ("if...") clause in

        it, which corresponds to the pushing-down of CONTEXT before the

        move to square is made.  When FORCEWIN is exited, none of the

        effects of ADD, REMOVE, or MAKEMOVE will be visible.  

            FORCEWIN illustrates several new Conniver features.  The

        macro-characters !" are used to signify ␈↓↓skeleton␈↓ ␈↓↓expansion␈↓.

        !"(*elements*) is like '(*elements*), except that some of the

        elements are evaluated and their values substituted into the

        result, so that it is not EQ to the original "skeleton."  Within

        a skeleton, "," indicates that the value of a Conniver variable

        is to be substituted.  Thus, if PLAYER=X and SQUARE=5, !"(HAS

        ,PLAYER ,SQUARE) has value (HAS X 5).  Other characters have

        other uses;  see Sect. VII.1.  

            (MAKEMOVE player) is the main tic-tac-toe player being called

        recursively; FORCEWIN is itself a subroutine of MAKEMOVE.  It

        adds to the data base the best move player can make.  (OTHER

        player) is defined as (OTHER X)=O and (OTHER O)=X.  
















                                                                   I.6 17





        I.6 Generators 



            The most important feature of FORCEWIN is the generalization

        it makes of possibilities lists.  The possibilities list WM

        contains a new kind of possibility, of type *GENERATOR, whose

        second element is a form, (WINMOVES PLAYER).  When TRY-NEXT sees

        such a thing, it evaluates the form, in this case calling the

        function WINMOVES.  

            Such a function might do anything.  If it just returns, TRY-

        NEXT just goes on to the next possibility (if any).  In the usual

        case, however, the function behaves like a ␈↓↓generator␈↓, adding new

        possibilities to the list in its place.  Thus, such a possibility

        "stands for" the "real" possibilities which the function is

        capable of generating.  In a rudimentary sense which will be

        expanded, the list of possibilities is a communication channel

        between the generator and the function that called it.  

            What WINMOVES wishes to communicate is all the winning moves

        its PLAYER has in the board position in which it is called.  If

        there are any, they are simply put into the possibilities list as

        numbers.  When TRY-NEXT sees an element of this sort (with no

        flag like *ITEM or *GENERATOR), it assumes it is a ␈↓↓value␈↓

        ␈↓↓possibility␈↓ and merely pops it off and returns it.  So, in this

        case, the last line of FORCEWIN means, "return a winning move for

        PLAYER, if any, else NIL." 

            The description of generators that is to come will introduce




                                                                   I.6 18





        and justify Conniver control structure.  Before I give it, let me

        summarize what has been created so far.  So far, Conniver is an

        ordinary Lisp-like language with a system-maintained data base.

        This data base has the interesting property that it consists of

        any number of contexts, related in a tree structure.  [It might

        look as if restricting them to be bound only to bindings of the

        variable CONTEXT would collapse this tree into a stack, of which

        only the topmost context would be important at any one time.

        This assumption will be shown to be false.] 

            A simple version of the generator WINMOVES would look like:  

        (CDEFUN WINMOVES (PLAYER) 
                          "AUX" (SQUARE1 P1 SQUARE2 P2 X) 
           (CSETQ P1 (FETCH '(HAS !,PLAYER !>SQUARE1))) 
        :OUTERLOOP 
           (TRY-NEXT P1 '(ADIEU)) 
           (CSETQ P2 (FETCH '(HAS !,PLAYER !>SQUARE2))) 
        :INNERLOOP 
           (TRY-NEXT P2 '(GO 'OUTERLOOP)) 
           (COND ((LESSP SQUARE1 SQUARE2) 
                  (COND ((CSETQ X (THIRD-IN-ROW SQUARE1 SQUARE2)) 
                         (COND ((PRESENT '(FREE !,X)) 
                                (NOTE X))   ))   ))   ) 
           (GO 'INNERLOOP)  ).  


            There are some new functions and notations to be explained

        here.  The new functions are COND, PRESENT, THIRD-IN-ROW, NOTE,

        and ADIEU.  (LESSP is a Lisp function, which is callable from

        Conniver.)  COND is the Conniver version of Lisp's COND, which

        differs from it only in that a COND clause, after the test form,

        is just like a PROG; "AUX" variables and statement labels are

        allowed; but these features are not used here.  (PRESENT pattern)

        is a system function almost like (TRY-NEXT (FETCH pattern)); it




                                                                   I.6 19





        returns an item and sets the pattern variables if there is one

        present that matches the pattern.  THIRD-IN-ROW returns the third

        square in a line with its arguments, or NIL if they are not

        collinear.   

            Before I describe NOTE and ADIEU, which allow WINMOVES to

        communicate with its caller through its calling possibilities

        list, let me explain the prefix "!," used in the three FETCH

        patterns (including that of PRESENT).  "!,PLAYER" in this program

        matches only the current Conniver value of PLAYER.  (It actually

        has a slightly more general meaning; see Chapter IV for a

        complete description of this and several other pattern prefixes.)

        The FETCHes in this program, like that of my earlier program for

        printing mothers of God, create possibilities lists (P1 and P2)

        of items, which are used in TRY-NEXT-driven loops.  Notice that

        P1 and P2 are substantially the same list, except that each sets

        a different variable, and that P2 is re-created more often than

        P1.  Each is a list of all items corresponding to squares owned

        by PLAYER.  They are used to generate all pairs of squares owned

        by PLAYER.  (The LESSP clause of the first COND is used to

        discard redundant or degenerate pairs. This is an inefficient,

        but clear, way of doing things.) 

            Whenever WINMOVES has found two collinear occupied squares

        with a free third collinear square, it must insert this third

        (winning) square in the possibilities list.  This it does with

        NOTE.  When all winning moves have been discovered, P1 will be




                                                                   I.6 20





        exhausted, and the TRY-NEXT at statement :OUTERLOOP will execute

        (ADIEU).   In this case, ADIEU merely returns to TRY-NEXT, which

        returns the first NOTEd winning square, if there are any.  





        I.7 Generalized Control Structure 



            The type of generator I have described so far is merely an

        odd function which is capable of returning zero or many values

        instead of just one.  As such, it is not very interesting.  In

        some cases, also, it is inefficient.  It may be, for example,

        much more expensive to generate possibilities than to use them;

        or the expense of generating them may grow as fewer and fewer

        remain; or the number may be infinite.  Another type of

        difficulty is that the generation of successive possibilities may

        depend on what the calling function did with previous ones; the

        caller may want to advise the generator as to how to proceed.  Or

        it may simply be that the generator has no idea how many

        possibilities its caller wants.  (Notice that FORCEWIN is

        interested in only one winning move.) 

            What is needed is a way of returning some of the

        possibilities while maintaining the generator in existence for

        further duty if required.  I will describe in a moment the AU-

        REVOIR feature that allows this to happen, but first the question

        must be answered, what is being maintained in existence?  In this




                                                                   I.7 21





        case, what is being saved is a description of the "process"

        embodied by the generator.  This description must include the

        values and names of the variables bound there, the body of the

        generator and where control was before it returned, and who it

        returned to.  All of this information is saved in the ␈↓↓frame␈↓ of

        the generator.  This frame is created when the generator is

        called (as it is for every function).  Normally, frames are lost

        when control returns from them; they are garbage-collected along

        with their bound variables, including any bindings of CONTEXT

        they may have.  This is why I said the context tree might look

        like a stack.  It is also why, in most programming languages,

        frames are stored on a stack ("frame" originally meant "section

        of stack"), and always go away when they are popped off.  

            In Conniver, however, frames are accessible data structures

        which can be protected from garbage collection merely by being

        pointed to.  Protecting a frame in this way means that its

        bindings must remain in potential existence, since they are

        always capable of being resurrected.  The ways in which such

        frames can be used will be described.  For now, notice that one

        implication of this design is that Conniver control and context

        structures must be at least as complex as trees (Cf. sect. II.1).

            One thing that can be done with frames is to make tags, which

        can be GOne to like atoms; however, GOing to a tag restores its

        bindings, no matter when they were created.  So, a generator can

        keep itself in existence by generating a kind of tag as a




                                                                   I.7 22





        possibility.  This tag stands for the further possibilities it

        can generate the way its original generator stood for all its

        possibilities.  Such a tag is called an *AU-REVOIR possibility,

        and is generated by calling (AU-REVOIR).  This form behaves like

        (ADIEU), but, by saving a tag to its own frame, can potentially

        return "again," inside the generator, causing it to note new

        values, and repeat the AU-REVOIR or do an ADIEU.  

            As an example, consider the following version of WINMOVES,

        which returns one winning move at a time:  

        (CDEFUN WINMOVES (PLAYER) 
                          "AUX" (SQUARE1 P1 SQUARE2 P2 X) 
           (CSETQ P1 (FETCH '(HAS !,PLAYER !>SQUARE1))) 
        :OUTERLOOP 
           (TRY-NEXT P1 '(ADIEU)) 
           (CSETQ P2 (FETCH '(HAS !,PLAYER !>SQUARE2))) 
        :INNERLOOP 
           (TRY-NEXT P2 '(GO 'OUTERLOOP)) 
           (COND ((LESSP SQUARE1 SQUARE2) 
                  (COND ((CSETQ X (THIRD-IN-ROW SQUARE1 SQUARE2)) 
                         (COND ((PRESENT '(FREE !,X)) 
                                (NOTE X) 
                                (AU-REVOIR))   ))   ))   ) 
           (GO 'INNERLOOP)  ) 

            The only difference is the introduction of (AU-REVOIR)

        following (NOTE X).  (This could have been abbreviated (AU-REVOIR

        X).) However, now a call to WINMOVES generates just two

        possibilities:  a winning move and a tag to the end of the AU-

        REVOIR.   

            If the tag is ever GOne to (by TRY-NEXT the second time it

        tries to pop off a winning move), AU-REVOIR will do a return ␈↓↓in␈↓

        ␈↓↓WINMOVES␈↓ and execution will proceed with (GO 'INNERLOOP).  The

        effect on TRY-NEXT will be that it will magically come up with




                                                                   I.7 23





        yet another winning move and tag.  Only when all winning moves

        have been generated can WINMOVES do an ADIEU, which leaves the

        possibilities list empty and causes TRY-NEXT to return its second

        argument.  

            These two examples do not exhaust the ways in which a

        generator may interact with a possibilities list.  For

        sophisticated problems, it will almost certainly be necessary for

        generators to inspect the POSSIBILITIES bound in the frame of

        TRY-NEXT, filter some of them out, add properties to them that

        the program looking at them should know about, or even take

        control of their generation by setting empty the POSSIBILITIES

        bound in the frame of the upper TRY-NEXT and itself calling TRY-

        NEXT on each of the possibilities, in order to accomplish some

        particularly complicated filtering.  The functions GET-

        POSSIBILITIES and SET-POSSIBILITIES enable a generator to access

        this binding of the list.  Clearly, in order for a user's program

        to edit a possibilities list, he must know the formats of the

        various types of possibilities;  these are given in the next

        chapter.  Communication the other way, from the ␈↓↓user␈↓ of the

        generated possibilities, is made possible by an optional message

        argument to TRY-NEXT that it sends to the generator, which is

        returned, in the generator's activation, as the value of AU-

        REVOIR.  All of these features are described in detail in the

        appendix (Sect. VII.1.8).  






                                                                    II 24





        II HAIRY CONTROL STRUCTURE 



            Chapter I has demonstrated that the control structures

        necessary to support things like generators are of a sort which

        are illegal in most languages.  Just how illegal will now be made

        clear.  





        II.1 What a Frame Is 

        II.1.1 How to Be a Programming Language 



            If you were to simulate a PROG, there are two things you

        would have to keep track of:  which line of the program you were

        working on; and what the current values of its "AUX" variables

        were.   If this PROG evaluated another one, you would have to

        stop what you were doing, and do something similar to the new

        one.   

            In evaluating the new one, however, there are two new things

        to remember:  which line of the old program to work on when you

        get back to it, and what the values of the "AUX" variables of the

        old program are:  

        (PROG "AUX" ((X 5) (Y 10)) 
           (PROG "AUX" ((X 50)) 
              (PLUS X Y)) 
           (PLUS X Y)).  







                                                                II.1.1 25





        The inner PROG requires the values of the outer X and Y for two

        reasons:  it must be able to refer to them free (as it does here

        with Y), and must be able to restore their values when it returns

        (as it does here for X).  Thus, the inner sum of this example has

        value 60; the outer, and the whole expression, 15.  

            A language interpreter has need of the same information.  It

        stores it in an object called a "fr," with four slots:  

            (1) BVARS (Bound VARiableS):  a pairing of a location with
        each bound variable name.  

            (2) IVARS (Internal VARiableS):  a specification of what the
        interpreter is now doing.  (For example, which line of a PROG it
        is working on, or which argument of a PLUS it is evaluating.) 

            (3) ALINK (Access LINK):  the fr whose BVARS and ALINK are to
        be searched for any free variables that are not bound in this
        fr's BVARS.  

            (4) CLINK (Control LINK):  the fr to which control is to
        return when it leaves this one.  The IVARS of CLINK specify how
        the running of that fr is to proceed.  


            We will represent a fr by a box, with bound variables

        indicated beside it, whose CLINK is an arrow pointing to another

        fr, and whose ALINK is a dotted arrow pointing to yet another

        one.  (Fig. 1(a).)  If ALINK(fr) = CLINK(fr), a double arrow will

        be used.  (Our terminology and notation are a simplified version

        of that of Bobrow and Wegbreit (1972).) 












                                                                II.1.1 26

















                                    Figure 1.

        The control structure during execution of the PROG's given before

        is shown in Fig. 1(b).  

            It might be asked whether anything more complicated than Fig.

        1(b) is necessary or possible.  In some languages, it is not.

        PL/I, for example, allows this structure and no other.  FORTRAN

        imposes the additional restriction that no two fr's in a chain of

        fr's be created by the same function; hence, Fig. 1(b) is not

        even possible in FORTRAN.  

            However, other structures are imaginable.  






















                                                                II.1.1 27





















                                    Figure 2.



            In Fig. 2(a), X has value 100 rather than 10 in fr A. The

        same is true for fr B of Fig. 2(b).  Fig. 2(a) is a legal

        structure in Algol, MAC Lisp, and Lisp 1.5. (In the last,

        however, access fr's and control fr's are different kinds of

        entities.)  Fig. 2(b) is legal in Lisp 1.5 only.  (These

        structures arise from the application of "FUNARG's";  see below,

        sect. 1.2.3.)  The other cases are unusual.  Fig. 2(c) shows the

        typical situation of a generator revived after an AU-REVOIR.  No

        one has yet thought of a use for Fig. 2(d).  

            These abstract objects may be implemented in various ways.

        In FORTRAN, a fr is not clearly distinguished from a function; in

        addition, each function has as ALINK only the COMMON area.  In

        most languages, fr's are implemented as stack frames, which can

        be piled up as Fig. 1(b).  Once such a frame returns control to

        its caller, that frame is no longer referenceable.  This




                                                                II.1.1 28





        condition rules out the structures of Fig. 2(b) and (c).  (In

        Lisp 1.5, the BVARS and ALINK of a fr are preserved after it is

        popped off, so Fig. 2(b) is possible.) 

            Notice that Fig. 3 











                                    Figure 3.

        is ambiguous, in that the two subfr's of C may be meant to be

        chronologically exclusive or not.  In the former case, A and B

        may share stack space; otherwise, as in Fig. 2(a), they may not.

        It is clear what the chronological interpretation of Fig. 3 is:

        C called A, A returned, then C called B.  What is the other

        interpretation?  Simply that A stands ready to begin execution

        again or supply its bound variables' values (as in Fig. 2(b)).  

            [We leave out a notation for IVARS in fr's of Fig. 3 and

        elsewhere, to make things simpler.  It is clear that A and B must

        see different IVARS for C, so that different things may happen

        when they return.  (There is nothing to prevent A from returning

        several times.)  Leaving out IVARS allows us to be vague about

        exactly which point in the execution of a fr we intend; remember

        only that each CLINK must specify its superior's IVARS.] 






                                                                II.1.1 29







        II.1.2 Conniver Control Structure 



            In Conniver, a fr is an internal list structure which

        specifies BVARS, IVARS, ALINK, CLINK, and EXP, the last being the

        form whose evaluation led to the creation of the fr.  Whenever a

        non-atomic expression is CEVALuated, a new fr is created.  (The

        only exception is FEXPR applications; Lisp EXPR's, SUBR's, etc.

        do get Conniver fr's.) 

            Conniver fr's are used as internal interpreter structures

        (i.e., parts of other fr's) as described, but they are also

        accessible to users as parts of frames, tags, closures, and *AU-

        REVOIR possibilities.  These concepts will be explained.  





        II.1.2.1 Frames 



            A ␈↓↓frame␈↓ is a structure of the form (*FRAME fr).  This is the

        external representation of an unadorned fr.  It may be used in

        two basic ways, for relative evaluation or continuation.

        Evaluation of an expression relative to a frame is a way of

        creating its frame with an abnormal access link (cf. Fig. 2(a)

        and (b)), namely the fr of the given frame.  (Another way to do

        this is with closures; see below.)  Relative evaluation is done

        with the function (CEVAL expression frame).  




                                                              II.1.2.1 30





            ␈↓↓Continuation␈↓ of a frame is returning control to it as

        directed by the IVARS of its fr.  This is done by (CONTINUE

        frame).  To CONTINUE from a frame's control link, use (EXIT value

        frame), which causes frame to return with value.  When no frame

        value is given, EXIT exits from the most immediately enclosing

        COND, PROG, CEXPR, or method body.  RETURN bypasses COND, so is

        often more convenient (See sect. VII.1.5).  

            Frames are created by the function (FRAME), which returns the

        frame of its caller.  (More precise and complete definitions of

        this and other functions are given in the appendix, sect.

        VII.1.4.) 

            For example, after the execution of 

        (PROG "AUX" ((X 50)) 
           (CSETQ GLOB (FRAME)) 
           (PRINT 'FOO)), 


        global variable GLOB will be bound to the frame of the PROG.

        (This is because FRAME returns a frame with the ALINK when it is

        called as its fr.) 

          Now (CEVAL 'X GLOB) has value 50.  (CONTINUE GLOB) causes FOO

        to be printed again.  

            Other functions can be used to manipulate Conniver frames.

        (ACCESS frame) and (CONTROL frame) return the frames for the

        ALINK and CLINK of frame's fr, respectively.  SETACCESS and

        SETCONTROL reset the appropriate links of frames.  For example,

        (SETCONTROL A A) causes the state of affairs shown in Fig. 2(d).






                                                              II.1.2.2 31







        II.1.2.2 Tags 



            Another way to use fr's is to create and use fr's associated

        with a labelled section of a PROG, function body, or COND clause.

        Such an object is called a ␈↓↓tag␈↓, and is of form (*TAG label fr),

        where fr is a frame whose IVARS instruct any CONTINUEr to begin

        execution at that labelled piece of body.  

            Such a tag is created using the function (TAG atom), which

        searches the current body for a tag of the form ":atom".  If it

        is not found, the CLINK of the current fr is followed, and the

        process repeated.  

            Tags may be used in any context that allows frames, including

        CEVAL and CONTINUE.  A synonym for CONTINUE with a tag argument

        is GO.  If GO has an atomic argument atom, it is equivalent to

        (GO (TAG atom)).  

            For example, the following toy program prints out FOO BAR:  




















                                                              II.1.2.2 32





        (CDEFUN PRINTFOOBAR () "AUX" (PLACE) 
           (COND ((CSETQ PLACE (ZOWIE)) 
                  (GO PLACE))   )) 

        (CDEFUN ZOWIE () 
           (PRINT 'FOO) 
           (RETURN (TAG 'PRINTBAR)) 
        :PRINTBAR 
           (PRIN1 'BAR) 
           NIL) 

        (PRINTFOOBAR) 

        and returns NIL.  (Note that GO always evaluates its argument,

        and expects an atom or a tag.) 





        II.1.2.3 Closures 



            Functions and other "procedural" objects (see below) may be

        associated with fr's to form ␈↓↓closures␈↓, data of the form (*CLOSURE

        function fr).  A closure behaves like its function, except that

        its frame will have ALINK=fr rather than ALINK=CLINK.

        Consequently, its free variables will be looked up using fr.

        This may give rise to a structure of form Fig. 2(a) or (b).  

            Closures are generated by (CLOSURE function), which returns

        the closure of function in the fr of (FRAME).  Since these

        objects may be returned or assigned to free variables, they may

        point to exited fr's, as in Fig. 2(b).  

            For example, the following function of X ␈↓↓returns␈↓ a function

        which adds X to anything:  





                                                              II.1.2.3 33





        (CDEFUN PLUSX (X) 
           (CLOSURE '(CLAMBDA (Y) (PLUS X Y)))).  


        Then, if F = (PLUSX 5), (CALL F 4) = 9.  When F is called, Y is

        bound to 4.  













                                    Figure 4.

            Closures can be used in any context as though they were the

        frames of their fr's.  Closures of methods are described below,

        Sect. II.2.  





        II.2 Methods 



            This flexible control structure can be used to provide an

        intimate association between a tree of problem-investigating

        Conniver processes and a tree of contexts.  In particular,

        procedures can be invoked by the addition or removal of an item

        to a context, by virtue of being linked to a pattern that matches

        the item.  Such ␈↓↓data␈↓ ␈↓↓base␈↓-␈↓↓sensitive␈↓ procedures are called

        ␈↓↓methods␈↓, of type ␈↓↓if-added␈↓ ␈↓↓if-removed␈↓, or ␈↓↓if-needed␈↓.  




                                                                II.2.1 34







        II.2.1 If-addeds and If-removeds 



            When an item is added (removed), any present if-addeds (if-

        removeds) whose patterns match the item are ␈↓↓invoked␈↓.  When a

        method is invoked, a new frame is created for it and the result

        of the match with its pattern (see Chapt. IV) is used to create

        its initial variable bindings.  (If the match fails, the method

        is, of course, not invoked.)  Auxiliary variables may be bound by

        including an "AUX" declaration at the beginning of the method

        body.   Execution begins at the front of the if-added's (if-

        removed's) body, right after the "AUX" if there is one.  For

        example, the (anonymous) method 

        (IF-ADDED NIL (HAS !>WHO !>SQUARE) 
                      ((REMOVE !"(FREE ,SQUARE)))) 

        with name NIL, pattern (HAS !>WHO !>SQUARE), and body ((REMOVE

        !"(FREE ,SQUARE))), automatically erases (FREE square) when it is

        asserted that (HAS someone square).  Its use as a bookkeeper

        could save a line in the function FORCEWIN (sect. I.5).  

            A method is itself a data-type stored in the context-

        structured data base, so it may be present only in the contexts

        the user specifies.  Methods are ADDed and REMOVEd just like

        items, and like items, ␈↓↓indexed␈↓ in the data base by their patterns

        (see sect. III.1.2.2).  The function IF-ADDED (IF-REMOVED)

        creates an if-added (if-removed) method with the pattern given by

        its first argument and the body given by the rest of them.  The




                                                                II.2.1 35





        above method can be put in the current context by 

        (REALIZE (IF-ADDED (HAS !>WHO !>SQUARE) 
                 (REMOVE !"(FREE ,SQUARE))   )) 

        and removed by UNREALIZing an object ␈↓↓EQ␈↓ ␈↓↓to␈↓ ␈↓↓the␈↓ ␈↓↓one␈↓ ␈↓↓added.␈↓ 

            This EQ-restriction means that an attempt by a user to re-

        read and ADD a file full of such anonymous methods (say, after

        editing a bug out of one) will put equivalent copies of all of

        them in the data base twice, all to be called twice when needed.

        To avoid this problem, an if-added (or if-removed) can be

        associated with an atomic name; thus 

        (ADD (IF-ADDED HAS-FREE (HAS !>WHO !>SQUARE) 
                 (REMOVE !"(FREE ,SQUARE))   )) 

        causes the atom HAS-FREE to be associated with the method (under

        the indicator DATUM), and to be passed around by the indexing

        routines.  Executing the above expression a second time will now

        cause the method to be re-constructed (in case it had bugs in its

        previous incarnation), and associated with HAS-FREE, but not to

        be re-indexed, because the atom is equivalent to the method in

        the eyes of the system, and therefore already present.  In fact,

        if (IF-ADDED HAS-FREE...) has been executed, 

                        (ADD 'HAS-FREE) 

        is equivalent to the ADD above (cf. sect. III.4).  












                                                                II.2.2 36







        II.2.2 If-neededs 



            The third method of data base-control structure interaction

        is by use of ␈↓↓if-needed␈↓ ␈↓↓methods␈↓, which cooperate as intimately

        with FETCH as if-addeds and if-removeds with ADD and REMOVE.

        Often there is a class of data items which are to be regarded as

        "present" in a context on the basis of some procedural criterion

        rather than by virtue of actually being there and FETCHable.  An

        if-needed can be used to associate such a procedure with the

        pattern of a typical item of the class.  Any if-neededs present

        in a context will be found by FETCH, if their patterns match its

        pattern argument, and stuck at the end of its possibilities list.

        They are invoked by TRY-NEXT when it comes to them in the same

        way ADD and REMOVE invoke their methods:  the result of a

        successful match (an alist; see Chapt. IV.) will be present in

        the possibilities list of the method; it will be used to start

        the bound variables of the method's frame, which are augmented by

        any "AUX" variables that may be around.  Execution begins in the

        method, which behaves like a generator function (see sect. 3)

        with respect to the possibilities list TRY-NEXT is working on.  

            Within an if-needed method, the function INSTANCE of no

        arguments returns an instantiation of the method's pattern, with

        all variables given their current values.  Then (NOTE (INSTANCE))

        (or simply (NOTE)) causes such a ␈↓↓note␈↓ ␈↓↓possibility␈↓ to be appended




                                                                II.2.2 37





        to POSSIBILITIES.  Since TRY-NEXT has the same effect on a note

        as on an item possibility, NOTE simulates the presence of that

        instance as an item in the current context.  ADIEU and AU-REVOIR

        work in the same way as before.  

            The patterns of if-neededs may use match characters which are

        not usually used elsewhere.  This is because if-needed patterns

        are matched against FETCH-patterns that may include other

        variables, whereas all other patterns are matched against

        constant list structures.  The most important such special prefix

        is "!<", variables prefixed which match only with expressions

        with variables when the method is entered.  When the function

        INSTANCE is called, the variables prefixed with "!<" will have

        been assigned by execution of the body of the method, and their

        values will be transmitted back to the variables that they

        matched in the FETCH-pattern.  

            For example, to express the idea that all dwarves are

        vicious, in such a way as to insure that FETCH finds all dwarves

        when it looks for vicious persons, one might execute 

        (ADD (IF-NEEDED VD (VICIOUS !<X) 
                  "AUX" ((P (FETCH '(DWARF !>X)))) 
           :LOOP (TRY-NEXT P '(ADIEU)) 
                 (AU-REVOIR (INSTANCE)) 
                 (GO 'LOOP)   )) 

        VD will be invoked when it is found by a call like (FETCH

        '(VICIOUS !>Y)), i.e., an attempt to generate vicious creatures.

        It would not be invoked by a call like (FETCH '(VICIOUS LYNDON)),

        because !<X will not match a constant like LYNDON.  This method




                                                                II.2.2 38





        notes one vicious dwarf each time TRY-NEXT is called.  (The

        matcher is explained in detail in Sect. IV.) 



        II.2.3 Closures of Methods 



            Methods may have closures just as functions do, and these,

        too, can be added to the data-base.  If such a method is invoked

        by a data-base change, control will be in a procedure with an

        access link that differs from that of its caller (like functional

        arguments in Lisp).  This raises the possibility of a process in

        an old environment being awakened by the addition of an item to

        its context, or the removal of one from it.  In fact, the

        function HANG can bring exactly this state of affairs about.

        (HANG is ␈↓↓not␈↓ a Conniver primitive.)  (HANG release expression)

        evaluates expression (typically a transfer or return), but only

        after ADDing a method closure that implements a test for the

        release condition.  This condition is of the form (IF-ADDED

        pattern) or (IF-REMOVED pattern).  If an item matching pattern is

        ever added (or removed, as the case may be), HANG returns as its

        value the frame of the process which was interrupted while adding

        (or removing) the item, with the side effect of assigning the

        variables of the pattern.  

            For example, 

                 (CSETQ F1 
                        (HANG '(IF-ADDED (WIN !>PLAYER)) 
                              '(GO 'FOO))) 





                                                                II.2.3 39





        goes to :FOO, but execution will resume with a return from HANG

        if anyone adds (WIN someone) to the data base, and PLAYER will

        have gotten value someone in the frame F1.  

            HANG can be defined as 

        (CDEFUN HANG (RELEASE EXPRESSION) 
                     "AUX" (HANGFRAME) 
           (CSETQ HANGFRAME (FRAME)) 
           (REALIZE (CLOSURE 
                       (CEVAL (CONS (CAR RELEASE) 
                                    (CONS (CADR RELEASE) 
                                          '((EXIT (FRAME) 
                                                  HANGFRAME)   )))))) 
           (CEVAL EXPRESSION (CONTROL))   ) 

        By adding the CLOSURE of the method, the HANG is assured of the

        continued existence of its activation.  When the pattern is seen,

        the method immediately causes the HANG to return for a second

        time (i.e., have its frame be exited), this time with value

        (FRAME), which will be the frame of the method closure's

        activation.  The method EXITs from the correct frame because it

        looks up the value of the variable HANGFRAME by searching up the

        access chain (see sect. II.1), which points off to the frame of

        the HANG in which it was closed.  Notice that, having added to

        the current context the closure that does these marvelous things,

        HANG CEVALuates EXPRESSION in ␈↓↓its␈↓ (HANG's) control frame, the

        frame of its caller, which is what the user presumably intended. 

            HANG thus exploits the fact that every frame has ␈↓↓two␈↓ superior

        frames it points to, an ␈↓↓access␈↓ frame used for free variable

        evaluation and atom tag searching, and a ␈↓↓control␈↓ super-frame that

        control is expected to return to.  (See sect. II.1) 




                                                                II.2.3 40





            The utility of method closures is somewhat reduced by the

        fact that it is dangerous to ADD closures of methods containing

        bindings of CONTEXT.  This is explained in sect. III.1.2.2.  





        II.3 Generators 



            The basic operation of generators was explained in Sect. I as

        an illustration of Conniver control structure.  Since we have

        explained more of it now, it is worthwhile to describe in detail

        how generators do what they do.  

            A generator is any process which communicates with another

        process through a possibilities list.  The list of possibilities

        is a communication channel which must be set up before the

        generator is called.  As we have seen, the generator is

        associated with that particular list by being found as a

        *GENERATOR or *METHOD possibility in it.  Once the function or

        method is called, it behaves like any other, except that the

        value it might RETURN is ignored;  all its important values are

        to be deposited in the list, as the data the possibility "stood

        for" to the TRY-NEXT.  This means that, unlike most functions,

        generators may return zero or more values, instead of exactly

        one.   

            The mechanics of this are simple.  While it is running, a

        generator can see an invisible variable bound to the rest of the




                                                                  II.3 41





        possibilities list, starting from where it was found.  NOTE,

        ADIEU, and AU-REVOIR put new possibilities into this part of the

        list, in the order in which they are called.  AU-REVOIR also

        leaves an *AU-REVOIR possibility, which is like a tag (sect.

        VII.1.8).   

            The real trick is in TRY-NEXT.  When it finds an AU-REVOIR

        frame in a possibilities list (while it is chewing on the list

        ␈↓↓after␈↓ the generator has returned), it replaces the control link

        in the top frame of the generator structure to point to the new

        TRY-NEXT, and just EXITs the AU-REVOIR frame.  This is how

        structures of the form of Fig. 2(c) come about.  Control stays in

        the generator on its second round, with all free variables as

        before, until it is ready to return again, when it will return to

        the new TRY-NEXT, and ultimately to the caller of that TRY-NEXT.

            A generator may return anything as a possibility.  However,

        there may be special types it wants to use, either one of the

        system-defined types, or something the user has made up.  These

        will have format (type-atom ...).  The system-defined types are

        *ITEM, *METHOD, *GENERATOR, *AU-REVOIR, *NOTE.  These are

        explained in detail in Sect. VII.1.8.  














                                                                  II.4 42









        II.4 Applications of Control Structure 



            This control structure is intended to be manipulable by the

        user.  HANG, for example, is written in Conniver, not Lisp. In

        this section I give two quick examples of the kind of

        manipulations that may be done easily with Conniving control

        structure.   



        II.4.1 Backtracking 



          The backtracking control structure primitives of Planner can be

        written fairly simply in Conniver as follows:  

        (CSETQ FAILURE-STACK NIL) 

        (CDEFUN FAIL () "AUX" (T1) 
           (COND ((NULL FAILURE-STACK) (PRINT 'FAILED) (GO EAR-1))) 
           (CSETQ T1 (CAR FAILURE-STACK)) 
           (CSETQ FAILURE-STACK (CDR FAILURE-STACK)) 
           (GO T1)   ) 

        (EAR-1 is explained under "Using Conniver," below:  (GO EAR-1)

        gets a program to the top level).  This version of Planner

        maintains a list FAILURE-STACK of environments to fail back to.

        The list is taken apart by FAIL, which pops off the next element

        and GOes to it.  The list is built by FAILSET:  

        (CDEFUN FAILSET (T) 
           (CSETQ FAILURE-STACK (CONS (TAG T) FAILURE-STACK))   ) 





                                                                II.4.1 43





        Note that since FAILURE-STACK is an ordinary Conniver variable,

        there may be local bindings of it, hence a complex structure of

        failure stacks bound at different levels.  

        (CDEFUN GOAL (PATTERN) "AUX" ((DATA (FETCH PATTERN))) 
        :GOALF 
           (FAILSET 'GOALF) 
           (TRY-NEXT DATA '(PROG (CSETQ FAILURE-STACK 
                                        (CDR FAILURE-STACK)) 
                                 (FAIL)))   ) 

        This version of GOAL obeys Conniver conventions for data base

        search, pattern format, etc., but behaves like the Planner

        version in that it responds to a failure by TRYing-the-NEXT

        matching datum unless there aren't any, in which case it

        continues the failure.  

            Clearly, the type of generator we are describing does not

        work with AU-REVOIR.  Instead, we must call SUCCEED explicitly to

        return:  

        (CDEFUN SUCCEED () 
           (ADIEU (INSTANCE))   ), 

        and generation of more than one instance is done by failing back

        to failpoints within the body of the if-needed method.  

            The two remaining functions simulate ASSERT and ERASE in that

        their effects are undone on failure:  

        (CDEFUN ASSERT (SKELETON) 
           (FAILSET 'ASSERTF) 
           (RETURN (ADD SKELETON)) 
        :ASSERTF 
           (KILL SKELETON) 
           (FAIL)  ) 







                                                                II.4.1 44





        (CDEFUN ERASE (SKELETON) 
           (FAILSET 'ERASEF) 
           (RETURN (REMOVE SKELETON)) 
        :ERASEF 
           (INSERT SKELETON) 
           (FAIL)   ) 

        KILL and INSERT are versions of REMOVE and ADD which do not

        search for and invoke if-removed or if-added methods; here they

        are used to undo the effect of ASSERT and ERASE before failure is

        allowed to propagate.  



        II.4.2 Multiprocessing 



            It is just as easy to create various types of multiprocessing

        in Conniver.  This comes in handy for building goal trees, for

        example.  The easy way to do this is often just to throw tags and

        frames around, but you may prefer a more rigid format.  The

        following little number treats all processes as atoms with a tag

        under the indicator PROCESS which indicates where control is to

        resume to continue execution of that process.  The atom CURPROC

        always has one such atom as its value:  

                (CSETQ CURPROC (GENSYM)) 

          Processes are created in association with a function of no

        arguments by the function CREATE:  

        (CDEFUN CREATE (FUNC) "AUX" ((NEWPROC (GENSYM))) 
           (PUTPROP NEWPROC (TAG 'APPLY) 'PROCESS) 
           (RETURN NEWPROC) 
        :APPLY 
           (CALL FUNC) 
           (CERR PROCESS TRIED TO RETURN)   ) 




                                                                II.4.2 45





            Processes so created may be resumed at any time with the

        following function:  

        (CDEFUN RESUME (PROC) 
           (PUTPROP CURPROC (TAG 'RESUME) 'PROCESS) 
           (CSETQ CURPROC PROC) 
           (GO (GET PROC 'PROCESS)) 
        :RESUME).  

        Processes never return (that would generate an error, as in the

        last line of CREATE), but resume each other back and forth as

        they wish.  To do this, they must know each other's names.  The

        simple scheme shown here doesn't explicitly allow for that kind

        of communication, but it is not hard to see how RESUME, for

        example, might be redefined so as to return the name of the

        process that ultimately resumes the process that called it.  

            To destroy a process, execute (REMPROP process-atom

        'PROCESS).  It will then be garbage-collected.  


























                                                                   III 46





        III HAIRY DATA STRUCTURE 



            The basic features of the Conniver data base are its ␈↓↓context␈↓

        ␈↓↓structure␈↓ and ␈↓↓indexification␈↓.  They allow the user to create sets

        of data which are fetchable by pattern, have property

        associations, and exist in different configurations.  

            Until the discussion of implementation in Sect. 3 of this

        chapter, the right way to think about contexts is as a tree of

        data bases.  Anything that is added (including properties; see

        sect. 2) to a given context is immediately present in it and all

        its daughters, and will be automatically present in all new

        daughters that are sprouted from it using PUSH-CONTEXT.  However,

        the effect is completely invisible in its super-contexts.

        Exactly the same applies to removal of data (or properties);

        whatever was removed is gone in sub-contexts, still around in all

        super-contexts that used to contain it.  The mechanism for all

        this will be explained in sect. 3.  

            In Chapter I, the data base was thought of as containing

        items, arbitrary lists that were present or absent in each

        context.  Such items are implemented as types of data, called

        item data, which can be referred to, as we have seen, by

        patterns.  In this chapter, we will look at how to refer to items

        by pointers to their data, and thus introduce the concept of

        manipulating a datum.  

            A ␈↓↓datum␈↓ is a system-maintained entity, which has




                                                                   III 47





        characteristics which depend on its type, and which is either

        present or absent in any given context.  The data we have seen so

        far include item data and three kinds of method.  These types are

        ␈↓↓indexable␈↓ data, which appellation refers to the peculiar pattern-

        directed way of accessing them.   I will return to consideration

        of the data index later (sect. III.1.2).  





        III.1 Types of Data 

        III.1.1 Objects 



            A more primitive kind of datum has the same behavior with

        respect to contexts, but has no pattern to be referenced by.  It

        is accessible, like any Lisp structure, by a pointer to it.  This

        is the ␈↓↓object␈↓.  

            Many people are completely mystified by the notion of an

        object datum, since they prefer to think of a data base as a set

        of known assertions.  The most simple way they can think of to

        access it is to ask something like (PRESENT '(BLOCK A)), which

        checks whether A is a block; or (FETCH '(BLOCK !>X)), which gets

        a possibilities list of things currently believed to be blocks.  

            However, this kind of a data base is a recent development in

        A.I.  A more primitive kind just assigns various symbols as

        properties of other symbols.  This type is sometimes simpler and

        more efficient to use.  The first step toward making this kind of




                                                               III.1.1 48





        data base context-dependent would be to invent a "symbol" which

        is "present" only some of the time.  This is part of the concept

        of object.  

            For example, a vision program, as it reconstructs a visual

        scene from a vidissector image, must consider more than one set

        of possible real-world objects, and decide what is really there

        on the basis of which is most consistent with the evidence.  This

        world might be modelled as a list of Conniver objects, only some

        of which are present in any context.  Thus, an object proposer

        might summarize its conclusions by adding a new data object to

        the list POSSIBLE-THINGS:  

        (CSETQ POSSIBLE-THINGS 
               (CONS (CSETQ NEW-THING (OBJECT '(R4 R5 R9))) 
                     POSSIBLE-THINGS)).  

        This form  creates a possible object, NEW-OBJECT, considered to

        consist of regions 4, 5, and 9.  (A realistic data structure

        would undoubtedly have to contain more information.)  This object

        looks like (*OBJECT (R4 R5 R9)), and has ␈↓↓structure␈↓ (R4 R5 R9),

        which the system ignores.  New objects are, of course, absent in

        all contexts.  

            To make this datum present in the current context, one

        executes (REALIZE NEW-THING); to make it absent, he executes

        (UNREALIZE NEW-THING).  The predicate REAL returns its argument

        if it is present, or NIL if it is absent; UNREAL, the opposite.  

            To illustrate the use of these primitives, imagine a data

        structure for tic-tac-toe as follows.  Let XS be a Lisp array of




                                                               III.1.1 49





        9 data objects like that above, such that  (XS n) is the X in the

        square n; let OS be a similar array of O objects.  With this data

        structure, the predicate (FREE square) can be defined thus:  

        (CDEFUN FREE (SQUARE) 
           (NOT (OR (REAL (XS SQUARE)) (REAL (OS SQUARE))))   ) 

        To put an X in square 5 (the center), for example, execute 

                       (REALIZE (XS 5)) 

        If this is done in a particular context, the board will "have an

        X in the center" in that context and all contexts sprouted from

        it.  By resetting or rebinding CONTEXT to a ␈↓↓higher␈↓ point in the

        branch, the "X" modelled as (XS 5) can be made to "vanish," as

        (XS 5) reverts to absence.  

            This simple introduction to objects leaves them rather

        uninteresting, since they have exactly one context-dependent

        property (presence or absence) and a context-independent

        structure.  In fact, the most interesting uses of objects involve

        more general properties, which are discussed in Sect. III.2.  



        III.1.2 Indexable Data 



            An object behaves like any normal Lisp structure.  You use it

        by having a pointer to it, to which functions and predicates are

        applied.  The only difference is that its properties depend upon

        the current Conniver context.  

            Another class of data have the same properties as objects:

        given a pointer to one, exactly the same operations (REALIZation,




                                                               III.1.2 50





        UNREALIZation, reality testing, etc.) can be performed.  However,

        the way you obtain a pointer to one is by use of ␈↓↓patterns␈↓.  That

        is, upon specification of all or part of a list structure

        associated with such a datum, Conniver will generate or

        regurgitate the data that ␈↓↓match␈↓ what is given.  These are called

        ␈↓↓indexable␈↓ ␈↓↓data␈↓, because the restoration of a datum from a

        description of a piece of it is not possible without the presence

        of an ␈↓↓index␈↓ to all data of its type.  

            The indexable data types implemented in Conniver are ␈↓↓item␈↓

        ␈↓↓data␈↓, ␈↓↓methods␈↓ (of several types), and ␈↓↓method␈↓ ␈↓↓closures␈↓.  



        III.1.2.1 Types of Indexable Data 

        III.1.2.1.1 Item Data 



            The obvious operations on item data have been described in

        Chapter I.  From that chapter, it should be clear that item data

        can always be referred to by their associated items or patterns

        that match them, using functions like ADD, REMOVE, PRESENT, and

        FETCH.   However, each of these functions returns pointers to the

        item data involved, the direct accessing of which is more

        efficient that access through the index.  

            For example, 

                (CSETQ D1 (ADD '(HERSHEY BAR))) 

        returns 

                ((HERSHEY BAR) (0 (0 . +))), 




                                                           III.1.2.1.1 51





        or something similar.  This structure has a CAR which is the item

        of interest, and a CDR which describes its properties in various

        contexts (see sect. III.3).  The whole thing is an ␈↓↓item␈↓ ␈↓↓datum␈↓,

        and is pointed to by the ␈↓↓item␈↓ ␈↓↓index␈↓.  This means that (in a sub-

        context this time) if (REMOVE '(HERSHEY BAR)) is executed, the

        same item datum can be found, certain mumbo-jumbo performed on

        it, and 

                ((HERSHEY BAR) (10 (1)) (0 (0 10))) 

        returned.  This is the exact same datum (it is EQ to the first),

        with its structure changed.  (As will be described below (sect.

        3), its "tail" indicates that it is still present in the original

        context, but absent in the sub-context.) 

            It takes effort to go from the structure (HERSHEY BAR), EQUAL

        to the item wanted, to ((HERSHEY BAR)...), which is EQ to its

        item datum.  However, in this case D1 is a pointer to this datum,

        so there is no reason why one cannot execute 

                (UNREALIZE D1) 

        in the same sub-context as the previous REMOVal, with exactly the

        same result.  In particular, REALIZE and UNREALIZE call if-addeed

        and if-removed methods respectively, just as ADD and REMOVE do.  

            In fact, ADD can be defined as 

                (REALIZE (DATUM item)) 

        where DATUM is a function that maps items into their associated

        item data.  (If an item datum was not previously indexed, DATUM

        generates one.)  REMOVE has a similar paraphrase.  




                                                           III.1.2.1.1 52





            For a description of the exact meaning of an item's being

        indexed, see below, III.1.2.2.  



        III.1.2.1.2 Methods and Method Closures 



            The use and creation of these objects is dealt with

        elsewhere, in Sect. II.2 and VII.1.2.  Some description of how

        they behave as ␈↓↓data␈↓ will be given here.  

            Any datum that starts with the atom *CLOSURE is treated as a

        closure.  If a datum starts with any other atom but *OBJECT, it

        is supposed to be a method, of the form 

                (type name pattern body ...), 

        where "..." is its tail (see sect. 3).  It can be called by TRY-

        NEXT or INVOKE (sect.  II.2), or (indirectly) by ADD and REMOVE.

        User-defined method types can be used in any way the user wishes.

        (Sect. VII.2.2.F) 

            If a method has a non-NIL atomic name, it is a special case

        of a ␈↓↓named␈↓ ␈↓↓datum␈↓ (sect. 4), and the name is uniformly used by the

        system and user to refer to it.  



        III.1.2.2 The Index 



            There are two senses in which an indexable datum may be

        "there."  One is the same as for object data:  "there" means

        "present in the current context."  This is implemented by the set




                                                             III.1.2.2 53





        of markers on the "tail" of every datum.  The other sense is

        "pointed to by the index," that is, findable by asking the index

        maintainer for potential matches to a pattern.  Indexable data

        will be ␈↓↓indexed␈↓ in this fashion only when they are present or

        have a ␈↓↓property␈↓ (sect. 2) in at least one context.  (The

        motivation for this is that one version of ((A B C)), a

        propertyless item datum, is as good as any other version of ((A B

        C)), so there is no point in making all of them unique.) 

            From the user's point of view, the index is just a list of

        all propertied data.  When a datum acquires presence or some

        other property, it is added to the list.  When all its properties

        are flushed or the contexts it has properties in are garbage-

        collected (see sect. 3), it is deleted from it.  

            When the data base is initialized, each index (one for items

        and each method type) is indeed just a list of its contents.

        However, when this list exceeds a certain size, it is broken down

        into ␈↓↓subindexes␈↓, according to the contents of the CAR's and CDR's

        of its elements.  Each subindex specifies an index for each

        different atom that appears in a slot.  These indexes are

        themselves indexified if they become too large.  

            Now when a pattern is given to the data base, it can isolate

        a small subset of candidates for matching before running the

        relatively costly matcher.  It does this by looking for a bucket,

        in the index, corresponding to each constant position of the

        pattern, and taking the smallest bucket it finds.  For methods,




                                                             III.1.2.2 54





        in each position, it must take into account the bucket for

        methods with a variable in that position.  

            Thus, for example, the function FETCHI, which fetches items,

        works as follows:  

            a. Search the item index for candidates.  

            b. Throw away all those absent from the current context.  

            c. Match the others and make up a possibilities list.  

            The indexer's efficiency depends on two numbers,

        *ATOMINDEXTHRESHOLD and *STRUCTINDEXTHRESHOLD, which determine at

        what size buckets of the two types (which we really didn't

        describe) are broken down.  No one knows what the "best" values

        are for the numbers, or what they depend on.  If you want to

        worry about this, talk to DVM.  

            The indexes point at item data and therefore keep them from

        being garbage-collected.  This is, of course, essential, since as

        long as the contexts that contain them are around, such data

        might have to be accessed by pattern.  However, this feature can

        lead to difficulty.  If an item datum has a property that

        includes a pointer to a context in which that property is

        accessible, the resulting circular structure (context -> datum ->

        property -> context; see sect. 3) will be uncollectable.  So some

        interesting properties (like "frame in which this item was

        added," if it points even indirectly to a binding of CONTEXT) are

        unfeasible.  

            The biggest bummer is that method closures almost always




                                                             III.1.2.2 55





        point to frames with a binding of context that includes the

        closure.  Thus the presence of many closures will choke up free

        storage irrevocably.  You might have to write your own special

        garbage collector to delete these data by hand.  





        III.2 Properties of Data 



            It is always possible to given any datum a name (sect. 4) and

        assert items about it, in order to record its context-dependent

        properties.  However, for reasons of efficiency (and because

        items are not always the best means of representing everything),

        it is also possible to associate ␈↓↓indicators␈↓ with ␈↓↓properties␈↓ on a

        datum, and retrieve them in a context-dependent fashion.  

            To associate indicator with property in context, use 

                (DPUT datum property indicator context).  

        This causes datum to have the pair (indicator property ...) on

        its tail, where "..." is garbage described in sect. 3.  This pair

        is returned. This property pair will be associated with the datum

        in all subcontexts of context unless it is removed or overridden

        in one of them.  

                (DGET datum indicator context) 

        returns the first indicator-property pair found on the datum by

        searching the context, its super-context, etc., or NIL if there

        isn't one.  Finally, 




                                                                 III.2 56





                (DREM datum indicator context) 

        does a DGET, but makes ␈↓↓all␈↓ pairs found with that indicator

        invisible in context and all its sub-contexts, so that future

        DGET's in them will return NIL (See sect. 3).  

            As an example of what can be done with property functions,

        consider the representation of the tic-tac-toe board as an array

        SQUARES of 9 objects.  Let each such object specify the occupant

        of the corresponding square in a particular context as its

        property under the indicator OCCUPANT; if it is empty, let the

        object be absent in that context.  Then FREE can be written 

        (CDEFUN FREE (SQUARE) (UNREAL (SQUARES SQUARE))   ) 

        and the occupant of a square in the current context might be

        found by 

        (AND (REAL (SQUARES SQUARE)) 
             (CADR (DGET (SQUARES SQUARE) 'OCCUPANT))) 

        which returns X, O, or NIL.  Then FORCEWIN could be written 

        (CDEFUN FORCEWIN (PLAYER SQUARE) 
                         "AUX" ((CONTEXT (PUSH-CONTEXT))) 
           (DPUT (REALIZE (SQUARES SQUARE)) PLAYER 'OCCUPANT) 
           (MAKEMOVE (OTHER PLAYER)) 
           (TRY-NEXT (GENERATE (WINMOVES PLAYER)) NIL)   ) 

            If the semantics of property-list manipulators does not quite

        fit your needs, there are more primitive functions, described in

        Sect. VII.2.4, which enable you to tailor-make your own versions

        of them.  










                                                                 III.3 57









        III.3 Implementation of Contexts 



            A context is profitably considered an abstract object whose

        only interesting characteristics are how it got started and what

        has been done to it and its superiors since.  However, for some

        purposes it may be useful to know that a context is implemented

        as a list of ␈↓↓context␈↓ ␈↓↓layers␈↓, each of which describes the

        differences between a context containing that layer and one not

        containing it.  Actually, it is of the form (*CONTEXT *layers*)

        for debugging purposes.  



        III.3.1 Presence and Absence 



            To be precise, a layer's functions are to indicate which data

        are to be thought of as ␈↓↓realized␈↓ in contexts containing that

        layer, and which such mentions by other layers are to be

        ␈↓↓cancelled␈↓ in contexts containing it.  The first function is easy

        to grasp:  every datum is present in a context if and only if a

        search up the context from most recently pushed to most global

        finds a layer that mentions that datum.  (Fig.  1.) 










                                                               III.3.1 58





















                                    Figure 1.

          However, if that mention has been cancelled when the datum was

        "unrealized" in some subcontext, it does not count.  

            To make this clear, imagine the following context structure:

















                                    Figure 2.

        The numbers are context-layer numbers.  Four contexts, c1 (20,

        10, 0), c2 (40, 30, 10, 0), c3 (30, 10, 0), and c4 (10, 0), are

        identified (besides the global context c0 (= (0))).  C4 is super-

        context of all but c0.  




                                                               III.3.1 59





            Now imagine that the structure of Fig. 2 is begun by a

        process that sprouts a sub-context from the global context c0,

        and hypothesizes that the object datum NEW-THING (cf. sect. 1.1)

        is really "there," in a visual scene.  (Fig. 3) 













                                    Figure 3.

        This causes NEW-THING to sprout a ␈↓↓tail␈↓ of ␈↓↓context-markers␈↓ (c-

        markers), so that it looks like (*OBJECT (R4 R5 R9) (10 (0 .

        +))), and is present in all sub-contexts of c4, actual and

        potential (The format of c-markers is described in detail in

        Sect. VII.2).  

            Now the process creates two sub-processes, each with its own

        context, in this case, c1 and c3:  


















                                                               III.3.1 60

















                                    Figure 4.

        Each process investigates the interaction of NEW-THING with

        previous parts of the scene.  The investigation in c3 leads the

        machine to doubt that NEW-THING is there, so it executes

        (UNREALIZE NEW-THING C3), making it absent there by "hiding" the

        fact that c4 mentions the object.  The object remains absent in

        any sub-contexts of c3 generated by further pushing. (Fig. 5) 













                                    Figure 5.

        Now NEW-THING = (*OBJECT (R4 R5 R9) (30 (1)) (10 (0 30))),

        indicating "present in all contexts with layer 10 except those

        with layer 30 as well." 

            Meanwhile, the process running in c1 still believes NEW-THING

        is there. Imagine that it discovers it to be the only possible




                                                               III.3.1 61





        supporter of another object which is known to exist, and hence

        that NEW-THING is certain to exist also.  It executes (REALIZE

        NEW-THING C0), which makes it present in all the contexts:  













                                    Figure 6.

        Now NEW-THING looks like (*OBJECT (R4 R5 R9) (30 (1)) (10 (0 30))

        (0 (0 . +))); that is, a cancellation applies only to

        outstanding, not future, realizations of a datum.  Later

        realizations will override it.  (If (REALIZE NEW-THING C4) had

        been executed, the same effect would have occurred, except for

        NEW-THING's remaining absent in c0.  Its tail would have

        consisted of (10 (0 . +)) again.) 

            Not all UNREALIZations cause cancellation.  If NEW-THING were

        UNREALIZED in c4 at the second step, its tail would be emptied

        rather than be made to contain, say, the c-marker (10 (1 10)).

        That is, c-markers never cancel themselves.  

            In any given context, the predicates REAL and UNREAL can be

        used to determine if a datum is present or absent.  REAL returns

        its argument if there are any outstanding (uncancelled) mentions

        of it in the current context branch, or NIL otherwise; UNREAL,




                                                               III.3.1 62





        the opposite.  

            All of these primitives operate by altering or examining the

        tails of their arguments, which consist of c-markers of the form 

                (lnum (refco . status) *property-pairs*) 

        where lnum identifies a context layer, status is +, NIL, or a

        list of canceling lnums, and property-pairs are explained below

        (cf. sect. VII.2).  



        III.3.2 Implementation of Properties 



            The association of properties with data is in some sense a

        generalization of the notions of presence and absence.

        "Presence" can be considered an indicator whose associated

        property is ignored.  It is either there or not there.

        Properties, on the other hand, have distinguishable values, so

        they may be overridden as well as cancelled.  

            Properties are implemented as cancellable "pairs" which are

        associated with c-markers.  If a c-marker on a datum looks like:

                (n (...)...(ind prop . status)...), 

        it means that datum has the ind-prop association in contexts with

        layer ␈↓↓n␈↓, except those which cancel it, as indicated by ␈↓↓status␈↓,

        which is "+" if there are no cancellations.  (Details are given

        in sect. VII.2.) 








                                                               III.3.3 63







        III.3.3 Context Layers 



            A context layer is implemented as a list (*LAYER lnum

        *data*), where lnum is its unique layer number, and data are all

        data with at least one occurrence of lnum in their tails.  The

        reason the layer must point to each such datum is that when it is

        garbage-collected, the magic Conniver garbage collector must be

        able to remove all these occurrences.  (Unfortunately, this means

        contexts point at the data they contain, so the loops mentioned

        in Sect. 1.2.2 are possible.) 

            Anyway, it is possible to loop through the layers of a

        context and the data in the layers, and thus apply some function

        to, say, all the data in a context.  



        III.3.4 Nonstandard Contexts 



            Most  contexts are generated by PUSHing and POPping -CONTEXT.

        These processes cause the generation and discarding of context

        layers.  Since individual layers are sometimes important, it is

        desirable to be able to string them together into new contexts.

        Then a layer which has a c-marker of non-NIL status on a datum

        will make that datum present in any context in which it appears.

        A layer whose number appears as a canceller of c-markers or

        properties can be thrown in to make certain data or their




                                                               III.3.4 64





        properties go away.  

            The only restriction on generating new contexts is that the

        layers must appear in order of decreasing lnums.  This is because

        operations like testing the visibility of c-markers and property

        pairs involve taking the intersection of two set of lnums (the

        status and the context layers), which is done faster with ordered

        sets.  

            The functions which create new contexts are described in

        Sect. VII.2.5.  





        III.4 Named Data 



            Occasionally it is convenient to refer to an arbitrary datum

        by an atom rather than a pointer to the datum itself.  Sometimes

        this is a matter of convenience.  Sometimes, as with functions,

        the datum must mention itself without being circular.  In the

        case of methods, as mentioned before (sect. II.2), one needs to

        be able to refer to them by an EQ name when redefining them in

        order to avoid having two around with the same pattern; an atom

        fills this need.  Item data are also occasionally to be thought

        of as EQ things:  an item that refers to another item wants to

        mean that particular thing, not a data structure EQUAL to it.  

            Of course, the user may use any ␈↓↓ad␈↓ ␈↓↓hoc␈↓ scheme he wishes to

        associate an atom with a datum.  If he knows atoms might have




                                                                 III.4 65





        data under the property FOO, the item (POSSIBLE FRAB) might mean

        "the item under indicator FOO on FRAB is possible." 

            Another possibility is to create a more intimate association

        between an atom and a datum, in which the system considers the

        atom to stand for that datum in every situation, much as an atom

        with an EXPR property always stands for the associated function,

        in forms, as an argument to APPLY, etc. in a Lisp program.  This

        association is needed for named methods, and has been extended

        for use with all types of data.  Any datum may be given a name

        with the function NAME-DATUM.  If it already has a name, the name

        will be changed.  This function alters every old system-

        maintained copy of the datum to be the new name, even down to its

        name slot if it is a named method.  Thus the index, context

        layers, etc., will point to the atom instead of the datum

        directly.   

            The other ways to give a datum a name are with the method-

        defining functions, and the function DATUM with two arguments.

        All of these are documented in the appendix, sect. VII.2.2.  

            The association of an atom with an item datum is a bit

        imperfect, because of a problem with the identity of a

        propertyless datum.  As mentioned in sect. III.1.2, an item datum

        with no properties is not pointed to by the index.  DATUM of

        something like (A B C) is a brand-new ((A B C)) each time if the

        item has no properties or presence.  This will be true even if

        there is an atomic name for a particular such propertyless datum.




                                                                 III.4 66





        If the thing did have properties, this atom would be what the

        system would return as the DATUM of its item, but if it is

        unindexed, there is no way to find the atom, and the system is

        fooled into returning a new non-atomic item datum each time DATUM

        is called.  Thus, if item (A B C) corresponds to ((A B C) (10 (0)

        (PLAUS  12 . +))), and you execute (DATUM '(A B C) 'FOO), then

        FOO will become associated with that datum, and (DATUM '(A B C))

        will thenceforth return FOO.  

            However, if the item datum is just ((A B C)), (DATUM '(A B

        C)) will be ((A B C)) no matter how many times you name it.  So

        be careful on the timing of naming data.  
































                                                                    IV 67





        IV ON PATTERN-DIRECTED INVOCATION 

            Methods can be invoked in association with adding items to,

        fetching items from, and removing items from the data base.  The

        invocation depends on a match between the method's pattern and

        the item, a match being an assignment of values to the pattern's

        variables that will make it EQUAL to the item.  Since when if-

        needed methods are called, it is necessary to match two patterns

        against each other, the matcher always returns a list of two

        alists that specify assignments of as many variables on both

        sides as possible.  If there is no match, NIL is returned.  

            The matcher may be called by (MATCH varpat datapat);  MATCH

        is asymmetric in that it is biased toward assigning variables in

        varpat to constants from the other side.  A pattern is a non-

        circular list structure with "variable parts" marked by the

        prefixes "!>" and "!,".  "!>var" must match a variable-free

        section of datapat.  (This restriction will always be met when

        patterns are matched against items.)  Matching "!>var" against

        something causes var to be bound on the alist for variables in

        varpat, with a value corresponding to what is matched.  For

        example, (MATCH '(FOO !>X) '(FOO BAR)) returns (((X BAR)) NIL).

        (Here, "NIL" is the alist for variables in (FOO BAR).) 

            The matcher is multi-level (that is, variables can occur

        below the top level of list structure), and dots are allowed in

        patterns, as (DINO DESI . !>X).  Hence, the pattern ((FREDS !>X)

        . !>REST) matches 




                                                                    IV 68





                            ((FREDS FATHER) WHISTLES)

                         ((FREDS FATHER) WHISTLES DIXIE)

                             ((FREDS GONE) HE SAID),

        generating association lists 

                         ((X FATHER) (REST (WHISTLES)))

                      ((X FATHER) (REST (WHISTLES DIXIE)))

                          ((X GONE) (REST (HE SAID))),

        respectively.  (The second lists are always NIL in these cases.) 

            When FETCH calls the matcher, it uses the varpat alist to

        make up an item possibility.  Thus, if items (SPIRO LIKES ROCKS)

        and (SPIRO LIKES DICK) are present in the current context, (FETCH

        '(SPIRO LIKES !>WHAT)) might return 


        ((*POSSIBILITIES (SPIRO LIKES !>WHAT)) *IGNORE 
           (*ITEM ((SPIRO LIKES ROCKS) (10 (0 . +))) ((WHAT ROCKS))) 
           (*ITEM ((SPIRO LIKES DICK) (0 (0 . +))) ((WHAT DICK)))).  


            TRY-NEXT takes the association lists from item possibilities

        and assigns the variables as they direct.  

            The other principal prefix is "!,", which refers to the

        current binding of its variable in the match so far, or the

        current Conniver binding, and matches its value.  For example,

        the pattern (GRANDFATHER !>X !,X) matches all items corresponding

        to people who are their own grandfathers.  

            Another frill is the ability to specify a ␈↓↓restricted␈↓ ␈↓↓match␈↓.

        If "!>" prefixes a non-atomic expression, its CDR is a list of

        forms that must all evaluate (in Lisp) to non-NIL after




                                                                    IV 69





        assignment of its CAR.  For example, !>(X (ATOM !,X)) matches

        only atoms, and !>(CREATURE (FEATHERLESS !,CREATURE) (EQ (NUMBER-

        OF-LEGS !,CREATURE) 2)) matches featherless bipeds.  If it is

        desired to bind and initialize a variable in its pattern's alist,

        one can write "!,(var initial-value)."  For example, (FUNCT-OF

        !>FORM !,(F (CAR !,FORM))) matches (FUNCT-OF (FACTORIAL 5)

        FACTORIAL) but not (FUNCT-OF (PLUS 2 2) MINUS).  Finally, if it

        is desired to specify an item shape without naming or saving its

        parts, the prefix characters "!>" can stand alone.  Thus, (FETCH

        '(FOO !>)) returns a possibilities list of items of the form (FOO

        x), but applying TRY-NEXT to it sets no variables.  

            If-addeds and if-removeds work nicely with MATCH.  To invoke

        one, Conniver applies MATCH to its pattern and the item that

        triggered it.  If the result is non-NIL, the varpat alist is used

        to start the variable bindings in the method's frame (which may

        be augmented by "AUX" bindings).  

            An if-needed method is really an entirely different kind of

        entity.  First, its match occurs at FETCH-time, its alists being

        saved in a *METHOD possibility until TRY-NEXT calls it.  Second,

        such a method is a kind of callable subroutine, which should be

        capable of more than verifying or achieving conditions

        represented by constant patterns.  In particular, one would like

        to be able to specify that a slot represents an ␈↓↓output␈↓ ␈↓↓variable␈↓,

        to be set by the method but ␈↓↓not␈↓ by the match.  This is

        accomplished by use of the prefix "!<";  "!<var" matches only




                                                                    IV 70





        expressions with variables (var being assigned to that

        expression, but only so the user can see what is going to happen

        on output).  

            Thus, when (NOTE (INSTANCE)) is called to create a value to

        be added to the possibilities list, MATCH is called again in a

        special (secret) way, this time with the FETCH-pattern as varpat,

        which treats the method pattern as an essentially ␈↓↓constant␈↓

        structure whose variable slots are to be filled as indicated by

        the values the variables have picked up in the method.  

            As an example, consider the method 


        (IF-NEEDED FIND-SUPPORTERS 
               (ON !>X !<Y) 
              "AUX" ((P (FETCH '(SUPPORTS !>Y !,X)))) 
        :LOOP 
              (TRY-NEXT P '(ADIEU)) 
              (AU-REVOIR (INSTANCE)) 
              (GO 'LOOP)   ).  


        This method will appear in a FETCH-possibilities-list generated

        by, e.g., (FETCH '(ON BOX1 !>B)), but not one generated by, e.g.,

        (FETCH '(ON !>A TABLE)), which is looking for "supportees" of an

        object called TABLE.  When FIND-SUPPORTERS is entered, X will

        have the value BOX1, and Y the value !>B.  At statement :LOOP, Y

        is set to the next supporter of BOX1.  One such supporter is

        returned each time the method is entered or re-entered.  

            Notice that this method has a completely different purpose

        from one with the pattern (ON !<X !>Y), which would find the

        "supportees" of a given object Y; or one with pattern (ON !>X




                                                                    IV 71





        !>Y), which verifies a support relation.  

            Occasionally, however, one wishes the decision of what to do

        in cases differing in this way to be made after the method is

        entered.  In this case, one can use the prefix "!?" which is

        ambiguous; it matches anything, but its variable is assigned if

        and only if it matches a variable-free expression.  The

        complementary ambiguity on the FETCH-side is handled by the

        prefix "!;" which means "!>" if its variable is unbound in the

        current match alist and unassigned by Conniver; or "!," if its

        variable is assigned or bound in the match alist but unassigned.

        These characters are typically handy in situations where a

        pattern is to be expanded according to its definition, regardless

        of exactly what is variable in it.  For example, if men are

        always to be treated as featherless bipeds, the following method

        does the conversion if one is looking for men or attempting to

        verify that something is a man:  


        (IF-NEEDED IS-MAN 
               (IS !?X MAN) 
           "AUX" ((P (FETCH '(IS !;X BIPED)))) 
        :LOOP 
              (TRY-NEXT P '(ADIEU)) 
              (COND ((PRESENT '(FEATHERLESS !,X)) 
                     (AU-REVOIR (INSTANCE)))   ) 
              (GO 'LOOP)   ).  


        It takes the place of the two methods that would be required

        without "!?" and "!;", which would have "!<" and "!>", or "!>"

        and "!," instead.  (Micro-Planner users please note that the

        micro-Planner prefix "$?" includes both ambiguities, and would




                                                                    IV 72





        take the place of all prefixes used in IS-MAN.) 

            Finally, some if-needed methods claim to be able to expand on

        the ␈↓↓syntactic␈↓ ␈↓↓forms␈↓ of calling patterns rather than to be able to

        generate items similar to those represented by those patterns.

        The corresponding method-pattern variables are signaled by the

        prefix "!'", which is analogous to the "'" of CEXPR bound-

        variable declarations.  "!'var" binds var to an expression

        without examining its internal structure in any way; its

        variables are neither substituted or bound.  For example, a

        method for causing (FETCH '(AND *conjuncts*)) to set the

        variables in the conjuncts correctly might look like:  


        (IF-NEEDED AND-EXPANDER 
               (AND . !'CONJUNCTS) 
              (COND (CONJUNCTS 
                        "AUX" ((P1 (FETCH (CAR CONJUNCTS))) 
                               (P2 (FETCH !"(AND . @(CDR CONJUNCTS)))) 
                               COP2) 
                     :LOOP1 
                        (TRY-NEXT P1 '(ADIEU)) 
                        (CSETQ COP2 (COPY P2)) 
                     :LOOP2 
                        (TRY-NEXT COP2 '(GO 'LOOP1)) 
                        (AU-REVOIR (INSTANCE)) 
                        (GO 'LOOP2)) 
                    ((AU-REVOIR (INSTANCE)))   )).  


        For example, if the items (GREEN BOX3), (GREEN BOX1), (GREEN

        BOX4), (ON BOX1 BOX2), and (ON BOX4 BOX5) are in the data base,

        repeatedly TRY-NEXTing (FETCH '(AND (GREEN !>X) (ON !,X !>Y)))

        will set X to BOX1 and Y to BOX2, then X to BOX4 and Y to BOX5,

        then quit.  The method's value is always (*NOTE NIL), because it

        never concerns itself with binding calling pattern variables.  (A




                                                                    IV 73





        more efficient implementation, by the way, is obviously

        possible.) 

            All the syntactic frills such as restrictions and bound

        variable initialization are legal in method patterns.  However,

        it is generally meaningless to restrict a "!<"-marked variable.

        If !<(X (ATOM !,X)) appears in a pattern, it is not clear what is

        being restricted.  It is certainly ␈↓↓not␈↓ possible by such a

        restriction to prevent ␈↓↓future␈↓ assignments of X to anything non-

        atomic.  All restrictions apply only at match time.  In the case

        of "!?", restrictions will be run only if the prefixed variable

        is assigned in the match.  
































                                                                     V 74





        V USING CONNIVER 

            Conniver is a remarkably friendly language to use, because

        its control structure is "open to the public."  The command

        CNVR}K typed at DDT causes Conniver to print out its version

        number, set up an initially empty global context assigned to

        GLOBAL, and print 

        EAR-1 
        ← 

        The "←" is printed out whenever Conniver wants input.  The ear it

        is listening with initially is EAR-1.  This is not a joke, but a

        tag into a READ-CEVAL-CPRINT loop at the top level.  Interacting

        with such a loop ought to be very easy for an experienced Lisp

        user; Conniver will attempt to CEVAL everything typed at it, and

        will print the result, formatting the output so that variables

        and special data types print in a lucid fashion.  

            If input is switched to a new file (using UREAD), masses of

        CEXPR's can be defined using 

                 (CDEFUN name (*variable-declarations*) *body*).

        "CFEXPR's", "CLEXPR's", or something similar, are not needed

        because of the flexibility of variable declarations.

        Declarations can be just a list of atoms, but the construction 

                            "OPTIONAL" *declarations*

        enables function to supply default values for missing trailing

        arguments.  For example, the declaration (X "OPTIONAL" (Y

        CONTEXT) Z) specifies one required and two optional arguments; if

        Y is missing, it receives the value of CONTEXT; a missing third




                                                                     V 75





        argument leaves Z rebound but unassigned.  

            If the last two elements of the declarations are 

                                   "REST" var

        var is bound to a list of the remaining arguments, each

        evaluated.   

            In place of a declared variable, the form (QUOTE var) may

        appear in any of the variable declaration slots, including "REST"

        'var.  This has the effect of blocking evaluation of the

        corresponding argument, or list of arguments in the case of

        "REST".  A FEXPR of one argument L in Lisp, therefore, has as

        counterpart a CEXPR with declaration ("REST" 'L).  

            (It should be pointed out that this entire variable

        declaration syntax was taken from MUDDLE.) 

            When a Lisp or Conniver error occurs, the system initially

        causes a Lisp READ-EVAL-CPRINT loop to be created as close to the

        error as possible.  (Such a loop is created by the function

        CERR.)  Within this loop, only Lisp evaluations can take place.

        If it is desired to continue from the CERR, type (RETURN value-

        desired).   Altmode-P is equivalent to (RETURN NIL).  Users'

        instances of CERR (and CBREAK, which prints a less alarming

        message) may do anything they wish with a returned value.  The

        system usually ends an error message with "// <something> <- ?"

        This means that (RETURN value) will cause that something to be

        value.  For example, if an attempt is made to evaluate X when it

        is unassigned, the message 




                                                                     V 76





                        UNASSIGNED VARIABLE X // VAL <- ?

        is printed.  If the user types (RETURN 5), X will get value 5,

        and the evaluation will proceed from the CERR.  If there is no

        obvious thing to be returned, the system will type "// GO ON?"

        Any value returned will be ignored, but if the user wishes to do

        his own patching and proceed, he may.  Finally, if there is

        nothing to be done, an attempt to proceed will land him in the

        nearest EAR loop.  

            Within such a context (or any piece of Lisp), if it is

        desired to return to the closest Conniver frame and create a

        listen loop, evaluate (EAR).  This creates a Conniver EAR-n loop,

        whose creation is signalled by the printing of 

        EAR-n 

        ← 

        which behaves just like the top-level one.  A variant function is

        NEAR, which returns to the nearest already-existing Conniver

        listen loop.  Finally, the function TOP flushes the entire

        existing control structure, resets the EAR-counter to 1, and

        starts up a new EAR-1.  (GO EAR-1) is similar, but can only be

        executed from Conniver.  

            The function BACKTRACE can be used to get a lucid summary in

        a CERR- or EAR-loop of the ␈↓↓control␈↓ pointer chain from the current

        Conniver frame upwards.  Variable values can be inspected,

        functions can be called, etc.  The functions UP, DOWN, and J can

        be used to move an EAR-loop around in the control structure.




                                                                     V 77





        This is handy for "editing" the stack, checking out variable

        bindings in the top-most activation of a recursive structure,

        etc.  (See sect. VII.3.1.) 

            Since a tag is a sort of frame for most purposes, a Conniver

        listen loop can be flushed by EXITing EAR-n.  The function

        (DISMISS frame) has been provided to exit from it with no

        particular value.  DISMISS takes (FRAME) as its default argument.

        Within a LISTEN loop, $P is equivalent to (DISMISS).  

            To stop a Conniver program externally, use control-H (}H).

        This will generate a READ-EVAL-PRINT loop (as it would in Lisp),

        which can be exited with $P.  From this loop, (EAR) will get you

        to Conniver, which remembers the Lisp expression it was working

        on.  DISMISSing this EAR-n will cause the Lisp evaluation to be

        re-attempted (not resumed).  

            Another way to interrupt a Conniver is with }A, whose action

        depends on the next character read.  In general, this character

        must have a ↑A ("uparrow A") property on its property list; this

        property should be a function of one argument (the character),

        which is called when "}A that-character" is typed at Conniver.

        If there is no such function property, a question mark is printed

        as }A's sole function.  

            There are several built-in system functions that are handled

        by this mechanism.  }AE causes (EAR) to be executed at the next

        interruptible place in Conniver; }AN, (NEAR).  }AT causes (TOP)

        to be executed immediately.  A Lisp READ-EVAL-PRINT loop may be




                                                                     V 78





        caused with }AL; this is equivalent to a CERR loop.  Two others

        do not cause listen loops:  }AF flushes the current input buffer

        if control is already in a READ, and is thus equivalent to a

        million rubouts; }AX prints the current expression being

        CEVALuated inside Conniver, and continues.  }AX is a way of

        checking up on what the evaluator is doing without stopping it.  

            The Conniver error system operates with the more general

        Conniver interrupt system.  The Lisp variable CINTERRUPT contains

        a list of expressions to be CEVALuated at the next opportunity.

        The function (CINTERRUPT exp) adds exp to the end of the list.

        It will cause it to be evaluated the next time control returns to

        Conniver.  (Besides the error system, if-added and if-removed

        methods also cause interrupts to happen.)  The function (ALLOW T-

        or-NIL) enables or disables all interrupts.  If interrupts are

        disabled, CINTERRUPT queues them until (ALLOW T) is executed.  

            In many places in Conniver }G or }X will cause Lisp to go to

        a very-top-level READ-EVAL-PRINT loop; in such situations they

        are equivalent to STOP (see below).  However, just as Lisp must

        protect itself from such things during garbage collection,

        Conniver disables all such Lisp interrupts when its data base is

        in a momentarily inconsistent state--i.e., when it is making

        changes to it.  At such times, there is no way to stop the thing,

        so don't give it, e.g., a circular list as a context.  

            You can get out of Conniver at any time by calling STOP.

        This leaves all Conniver structures intact, but puts you in a




                                                                     V 79





        Lisp READ-EVAL-PRINT loop.  To restart in ␈↓↓exactly␈↓ ␈↓↓the␈↓ ␈↓↓state␈↓

        before (STOP), call (RUN); you're back in Conniver.  (RUN and

        STOP have more sophisticated uses; see the appendix.) 
















































                                                                    VI 80





        VI LISP AND CONNIVER 

            Lisp functions do not usually call Conniver CEXPR's, and

        CINT's (the analogue of FSUBR's in Lisp), because Lisp stacks are

        far more perishable than Conniver's frame-trees.  (But see the

        description of CEVAL, below.)  Conniver can call any Lisp

        function, though, and Lisp EXPR's, LEXPR's, LSUBR's, and SUBR's

        can take Conniver arguments in forms evaluated by Conniver.  For

        example, 

                            (PRINT (TRY-NEXT P NIL))

        is perfectly legal.  Lisp functions called by Conniver can

        reference Conniver variables free by use of the function (/,

        var), abbreviated ",var".  For example, Lisp functions should

        refer to CONTEXT as ,CONTEXT.  

            EAR, NEAR, STOP, and other Conniver system functions use

        labeled CATCHes and THROWs to do non-local control jumps.  The

        way Lisp currently works, there is no way to prevent unwanted

        interaction with users' unlabeled CATCHes should they be in the

        way.  For example, (CATCH (NEAR)) will return something

        meaningless rather than go to the nearest ear.  

            Since Lisp can't call CEXPR's, functions that do Conniving

        things must be written in Conniver down to a low level.  The

        resulting slowdown may make one cringe, but there is a remedy.

        Any piece of pure Lisp may be made more efficient by prefixing it

        with the "@" macro-character, and making all Conniver variable

        references explicit by use of ",".  For example, 




                                                                    VI 81





                        @(THIRD-IN-ROW ,SQUARE1 ,SQUARE2)

        where THIRD-IN-ROW is an EXPR, is much more efficient than 

                         (THIRD-IN-ROW SQUARE1 SQUARE2)

        because it expands into 

                  (/@ THIRD-IN-ROW (/, SQUARE1) (/, SQUARE2)),

        /@ being a ␈↓↓FEXPR␈↓, namely 

                           (LAMBDA (EXP) (EVAL EXP)).

        Conniver always gives FEXPR's complete control over their

        argument evaluation, so just hands the expression (/@ ...) to

        EVAL, saving generating a frame and interpreting the expression.

        The @ macro is thus a way of hand-compiling arbitrary sections of

        code involving no CEXPR's or CINT's.  Another use of the @ macro

        is getting the Lisp value of a variable within Conniver;

        @CONTEXT, for instance, gets the Lisp value of CONTEXT, just as

        "," gets its Conniver value.  

            A Lisp program, if it really wants to, can use CEVAL to

        Conniver-evaluate a form.  If it is a well-behaved form, this is

        just like using EVAL, but there are pitfalls.  Some of the

        problems stem from the fact that the frame and its daughters

        generated by execution of the form may hang around (with a HANG,

        for example), after an EXIT back to the Lisp.  While control is

        in this structure the first time, Lisp variables bound in its

        caller may be accessed (with @), and in general everything is

        cool.  After it returns, however, the Lisp return point vanishes,

        along with its frame, bindings, etc., and even the frame of the




                                                                    VI 82





        EXPR CEVAL.  

            If control re-enters the Conniver structure, the new Lisp

        stack-state above it will have nothing to do with the original,

        none of the old, now unbound, Lisp variables will be

        referenceable, and a return from the structure's top level will

        have no obvious meaning.  This is not to say that a process

        created in this manner has no use, but merely to emphasize the

        dangers in creating one.  Attempting to @ a Lisp variable will

        probably find it unbound (creating a Lisp-error in Conniver), and

        an attempt to return from the control structure again causes the

        entire Conniver to return to Lisp (thinking it is returning to

        the Lisp frame that started the CEVAL), in such a way that

        RUNning or STARTing is impossible. 

            There is still another problem which is even worse.  If,

        during a CEVALuation, control leaves the new Conniver control

        structure it created (e.g., by GOing to an old tag), and never

        returns, the entire old Conniver process will be running with a

        Lisp stack slightly different from what it started with.  In

        particular, all the Lisp frames that were around when CEVAL was

        called are still there, but there is no way to detect or flush

        them.  In such a situation, STOP (see Appendix) no longer does

        the right thing, and the stack has been enlarged in perpetuity.

        Enough such pathological CEVALs can cause a pdl overflow.  The

        user is strongly encouraged to use RUN and STOP for Lisp-Conniver

        interaction, even if they are trickier.  




                                                                    VI 83





            One pleasant thought is that many Conniver functions are

        actually EXPR's, or have EXPR versions which do almost the same

        thing.  (In the compiled Conniver system, of course, these are

        SUBR's or FSUBR's, but I will continue to use the term EXPR in

        the loose sense "Lisp function.") For example, the CEVAL you get

        if you call it from Lisp is clearly different from the CINT

        version the Conniver interpreter would find.  All functions with

        EXPR versions can, of course, be called from Lisp.  Happily, they

        include all the data base-manipulation functions, but the EXPR

        versions of ADD, REMOVE, REALIZE, and UNREALIZE differ slightly

        from the CEXPR versions because the invocation of any if-added or

        if-removed methods must be CEVAL'ed.  Since if-addeds and if-

        removeds are probably not too closely linked with the process

        that triggers them, these are probably safe CEVALs.  

            One worry the user ␈↓↓doesn't␈↓ have to have is whether his Lisp

        functions will clobber or rebind internal Lisp variables used by

        the Conniver interpreter.  All Conniver atoms Conniver doesn't

        want you to see have been "half-killed" in such a way that they

        will print out but cannot be recognized during user input.  
















                                                                   VII 84





        VII APPENDIX:  THE CONNIVER REFERENCE SOURCE 



                Whereas the previous sections of this manual are a

        discursive overview of Conniver for the purpose of illustration

        of and introduction to the ideas embodied in Conniver, this

        section is an attempt to provide a reference source for the

        active user.  Thus, it contains a detailed description of each

        primitive of the language, enumerating the possible error

        conditions that are associated with that primitive and its

        limitations which might not be immediately apparent.  Besides

        primitive operators, every language has a set of reserved words

        (syntactic indicators and significant variables).  These will be

        duly noted.  

            This appendix has three sections, one describing the

        evaluator, one for the data base functions, and one for various

        debugging aids.  Every function defined has its type (or types)

        specified next to a sample call.  CINTs and CEXPRs are invisible

        from Lisp and thus are only defined in Conniver code, avoiding

        interference with Lisp functions of the same name.  
















                                                                 VII.1 85





        VII.1 The Evaluator 



                The Conniver interpreter evaluates expressions in a

        manner similar to that of Lisp.  The basic syntax is as follows:



           ␈↓↓conniver␈↓←␈↓↓expression␈↓ = number ↑ atom ↑ 

                        's-expression ↑ @s-expression 

                        ↑ !"s-expression ↑ (function *arguments*) 

        where the arguments are themselves Conniver expressions (and zero

        is a possible number of them).  



        The evaluation rules are:  

          1. As in Lisp, quoted expressions and numbers evaluate to

        themselves.   

          2. The value of an atom is its value as a variable.  This value

        will be determined by its value from some local binding or a

        hidden list of global values.  

          3. An expression following an @ is passed directly off to Lisp

        for evaluation.  We recommend that code be written so that as

        much as possible happens in Lisp because of the considerable

        speedup attainable.  

          4. An expression following !" is called a ␈↓↓skeleton␈↓, which is

        ␈↓↓expanded␈↓ as follows:  atoms expand to themselves; ",atom" expands

        to the Conniver value of atom; "@expression" expands to the Lisp

        value of expression; "(!@expression . rest)" expands to (APPEND




                                                                 VII.1 86





        Lisp-value-of-expression expansion-of-rest); "(exp1 . rest)"

        expands to (CONS expansion-of-exp1 expansion-of-rest).  For

        example, if X= (A B C), !"(,X D @(CDR ,X) (!@(CDDR ,X) . ,X)) =

        ((A B C) D (B C) (C A B C)).  

          5. Functional applications are processed as follows:  

            If the function is atomic, it is checked for CINT, CEXPR,

        FEXPR, FSUBR definitions.  If an atom has two such definitions,

        the first on its property list is taken; this means that if the

        user wants a function to be a FEXPR in Lisp code and a CEXPR in

        Conniver code, the CEXPR must be defined last so as to be first

        on the property list.  If it is none of the above, it is assumed

        to be a Lisp EXPR, SUBR, or LSUBR, thus undefined function errors

        come from Lisp.  

            If the function is a FEXPR or FSUBR the form is passed to

        Lisp for immediate evaluation.  In this case, Conniver does not

        define a new frame for its evaluation.  

            In all other cases, a new frame is created, with appropriate

        access and control links.  

            If the function is a CINT (such as COND) the form is

        evaluated by the appropriate internal Conniver routine.  

            If the function is a CEXPR, the arguments are paired with the

        formal parameters of the function (and perhaps evaluated) as

        specified by the declaration in the function (see CDEFUN for

        details).  After binding, the body of the function is executed.  

            If the function is an EXPR, SUBR, or LSUBR the arguments are




                                                                 VII.1 87





        evaluated by Conniver and then the Lisp function is applied to

        the resulting argument list with Lisp APPLY. 

            If the function is non-atomic then either it is an anonymous

        CLAMBDA expression (CEXPR) or it is an anonymous LAMBDA

        expression (EXPR) and treated accordingly.  

            Note that there are no other cases.  The function position is

        never evaluated as in Lisp.  Functional arguments are handled

        explicitly, preventing ambiguity, using the function CALL.  



            Execution of the body of a CEXPR, PROG or METHOD proceeds as

        follows:   

            If it begins with the reserved word "AUX", then the auxiliary

        variables that follow it are bound.  (See below, Sect. VII.1.2.) 

            The rest of the body is then executed sequentially (unless

        the sequence is changed by a GO).  The value of the body (and

        hence of the function) is the value of the last expression in the

        body, unless a return is forced by RETURN, EXIT, or DISMISS.  




















                                                                 VII.1 88





        VII.1.1  Communication with Lisp 



        A. (RUN [stuff NIL])                            LSUBR 
           (STOP [stuff NIL])                           LSUBR 

            When a Conniver program is running, its control structure is

        "serviced" by a set of Lisp programs which use the Lisp stack.

        Control repeatedly returns to one called RUN1, which is the

        "inner loop" of the system.  Beneath the frame of RUN1 on the

        stack is its caller, which is expecting a value to be returned.

        The function STOP can be used to return such a value.  

            When it does, the state of the Conniver computation is not

        disturbed, because it must all be saved in various frames anyway.

        STOP leaves everything in such a state that (RUN x) will cause

        the Conniver to start again, as though STOP had returned x.  RUN

        does this by calling RUN1.  Hence, a later (STOP y) will make

        RUN1 return to RUN with value y.  RUN returns this value.  

            Hence, these functions allow Lisp and Conniver programs to

        treat each other as co-routines.  Control is passed from Conniver

        to Lisp via STOP and from Lisp to Conniver via RUN.  The argument

        to STOP is returned as the Lisp value of RUN and the argument of

        RUN is returned as the Conniver value of STOP.  STOP may only be

        called if Conniver is running, otherwise:  

                CONNIVER NOT RUNNING--STOP 

        RUN may only be called if Conniver is not running, otherwise:  

                CONNIVER ALREADY RUNNING--RUN1.  

        Example:  To have Conniver evaluate expressions passed to it from




                                                                 VII.1 89





        Lisp, we put Conniver into the loop:  



                (PROG "AUX" ((MESSAGE 'HI-LISP)) 
                 :LOOP (CSETQ MESSAGE (CEVAL (STOP MESSAGE))) 
                        (GO 'LOOP)) 


        Conniver returns to Lisp with the value HI-LISP.  Thereafter Lisp

        may get an expression evaluated by Conniver by calling 

                (RUN expression) 

        The value of RUN will be the Conniver value of the expression.  

            Within a (Lisp) CEVAL, STOP causes its argument to be

        returned as the CEVAL's value; this will be true even if Conniver

        control has left the structure that CEVAL set up.  RUN will ␈↓↓not␈↓

        get the program back to the execution point of that STOP, because

        after leaving the CEVAL, Conniver is already running.  So, be

        careful with using STOP to return a value for a CEVAL.  



            If, for some reason, the Conniver interpreter (not the data-

        base -- see DATA-INIT) needs to be re-initialized, it can be done

        by executing (from Lisp only):  

        C. (START)                                      SUBR 

        START resets all of the Conniver internal variables (including

        the ear) and goes into the top-level listen loop.  Global

        Conniver variable bindings and their values are not changed.  The

        data base is not disturbed, but all contexts previously saved

        only as values of Conniver variables will be lost to garbage

        collection.   




                                                                 VII.1 90





            When in Conniver IBASE=BASE=10. and all character macros are

        in effect; these return to their Lisp defaults when returning via

        STOP.  
















































                                                               VII.1.2 91





        VII.1.2 Procedure definition 



            All of the functions below define procedures which include a

        slot called the "body."  The body of a procedure is evaluated as

        follows:  The value of a function is the value of the last

        expression in the body (or of a RETURN, EXIT, or DISMISS).  The

        body is just a sequence of expressions to be evaluated.  If it

        begins with "AUX" (a reserved word) then the next element of the

        body is taken as a declaration of auxiliary variables (PROG

        variables in Lisp).  Such a declaration is a list of atoms and

        initializations.  Each atom is bound but left unassigned.  An

        initialization is a list of an atom and an expression.  The atom

        is bound and assigned to the value of the expression.  This

        expression must not evaluate to a tag or frame for the current

        activation of the procedure in which the "AUX" appears.  To

        initialize a variable to a tag, you must allow it to be bound to

        *UNASSIGNED, then CSETQ it to the tag value desired.  

        A. (CDEFUN 'atom 'declaration '*body*)         FSUBR 

            This function is used to define the atom to be a function

        with the formal parameters specified by the declaration and with

        the body given.  The definition will be placed on the atom's

        property list under the indicator CEXPR.  The body is simply a

        sequence of statements to be evaluated sequentially.  It may (or

        may not) begin with a declaration of auxiliary variables

        (described later). The formal parameter declaration syntax is as




                                                               VII.1.2 92





        follows:   

             declaration = (␈↓↓obligatory␈↓←␈↓↓variables␈↓ ␈↓↓optional␈↓←␈↓↓variables␈↓

        ␈↓↓excess␈↓) 

             ␈↓↓obligatory␈↓←␈↓↓variables␈↓ = ␈↓↓empty␈↓ ↑ ␈↓↓par1␈↓ ... ␈↓↓parN␈↓ 

             ␈↓↓parI␈↓ = atom ↑ 'atom 

             ␈↓↓optional␈↓←␈↓↓variables␈↓ = ␈↓↓empty␈↓ ↑ "OPTIONAL" ␈↓↓op1␈↓ ... ␈↓↓opN␈↓ 

            ␈↓↓opI␈↓ = atom ↑ 'atom ↑ (atom default) ↑ ('atom default) 

            ␈↓↓excess␈↓ = ␈↓↓empty␈↓ ↑ "REST" atom ↑ "REST" 'atom 

        The semantics is as follows:  

          1) Formal paramaters are matched against arguments from left to

        right.  

          2) There must be at least one argument for each obligatory

        variable.   

          3) Unless there is an excess collector declared, there may not

        be more arguments than declared variables.  

          4) Arguments are evaluated unless the corresponding formal

        parameter is quoted (').  

          5) If the arguments run out while binding optionals, they are

        filled with either *UNASSIGNED, or if an expression for the

        default value is given, the value of the default expression (in

        the frame of the function with all previously processed variables

        bound) is used.  

          6) An excess collector gets the list of  arguments or values of

        arguments (depending upon the existence of a ') left over.  

        This elegant syntax is due to Chris Reeve of MUDDLE.  Note how




                                                               VII.1.2 93





        beautifully this does away with FEXPR's and LEXPR's and how much

        more flexible than Lisp it is.  

            If the evaluator is not satisfied that the number of

        arguments is right for a function it prints either 

                TOO FEW ARGUMENTS--VARS = remaining vars // ARGS <- ?  

        and wants arguments for the leftover variables, or 

                TOO MANY ARGUMENTS--ARGS = remaining args // VARS <- ?  

        and wants variables to bind the leftover arguments to.  If the

        syntax of a declaration is not as specified above the error

        comment:   

             BAD DECLARATION--VARS = rotten variables // VARS <- ?  

        will be generated, and anything RETURNed will replace the rotten

        variables.   



        To create a method we use one of the constructors:  


        B. (IF-ADDED ['atom] 'pattern '*body*)          FSUBR 
           (IF-REMOVED ['atom] 'pattern '*body*)        FSUBR 
           (IF-NEEDED ['atom] 'pattern '*body*)         FSUBR 

        The given atom is defined or redefined to be a method of the type

        indicated, invoked by the given pattern, with the given body.

        The method required is the value of the constructor.  If the atom

        is not specified, the method is not named, but of course, it may

        be saved as the value of a variable.  To be accessible by

        pattern, a method must be put into the data base via INSERT or

        ADD.  Once a named method has been added to some contexts,

        redefinition of the same name will cause it to remain present in




                                                               VII.1.2 94





        the same contexts.  

            The pattern is the analogue of the variable declarations of a

        CEXPR; in particular, the appearance of any type of match

        variable (except "!,var") signals that variable is to be bound

        when the method is invoked.  The pattern is used as described in

        Chapter IV.  










































                                                               VII.1.3 95





        VII.1.3 Sequence Evaluators 



        A. (PROG ["AUX" 'aux-variable-declarations] '*body*) 
                                                        CINT 
           (PROGBIND aux-variable-declaration '*body*)  CINT 

            The value of a Conniver PROG is the value of the last

        expression in its body.  The expressions in the body are

        evaluated in order after any "AUX" variables are bound.  These

        variables are subject to the same format and restrictions as

        those for CEXPR's and methods.  The sequence of evaluation may be

        altered by use of GO (Sect. VII.1.5).  

            PROGBIND is like PROG except that it evaluates its first

        argument to give a list of auxiliary variables.  For example, if

        X=A, 

             (PROGBIND (LIST (LIST X 5)) (PRINT A)) 

        binds A to 5 and prints "5".  



        B. (COND 'clause1 ... 'clauseN)                   CINT 

        COND in Conniver is almost identical to COND in Lisp except for

        the fact that the CDR of a clause is a general PROG body.  Thus

        it may contain an "AUX" declaration (See Definition of

        procedures, PROG) and statement labels (tags).  Thus entering a

        COND clause produces a new activation block so remember this when

        using EXIT etc.  This is a legal use of COND:  








                                                               VII.1.3 96





                (COND ((= N 1) "AUX" ((M 2) P) 
                                      (CSETQ P (ACTBLOCK)) 
                         :LOOP (COND ((= (CSETQ M (1- M)) 0) 
                                       (EXIT 3 P))) 
                                (GO 'LOOP)) 
                       (T 2)) 


        C. (AND '*body*)                                CINT 
           (OR '*body*)                                 CINT 

            These are exactly equivalent to their Lisp counterparts.  AND

        returns the value of the last element of its body or NIL if one

        of the elements evaluates to NIL.  (AND) = T.  OR returns the

        value of the first non-NIL element of its body, or NIL if all of

        its elements evaluate to NIL.  (OR) = NIL.  


































                                                               VII.1.4 97





        VII.1.4  Frame Creators and Manipulators 



            Conniver keeps track of what it is doing by maintaining a

        structure called a ␈↓↓fr␈↓ for each invocation of all kinds of

        functions except Lisp FEXPR's or FSUBR's.  A fr is basically a

        structure with five slots:  IVARS, BVARS, FORM, ALINK, and CLINK.

        IVARS are the internals of the interpreter; BVARS are the

        variable locatives for the variables bound in the frame; FORM is

        the expression whose evaluation gave rise to this frame; and

        ALINK and CLINK are the access and control fr's where free

        variables will be looked up and where control will return,

        respectively.   

            These objects are not explicitly touchable by the user, but

        are parts of frames, tags, and closures, the data types returned

        or manipulated by the functions of this section.  



        A. (FRAME)                                      SUBR 
           (ACCESS [frame (FRAME)])                     LSUBR 
           (CONTROL [frame (FRAME)])                    LSUBR 
           (EXPRESSION frame)                           SUBR 

            FRAME returns the frame with respect to which it was

        evaluated.   This means the nearest enclosing frame in which

        variables are bound.  This means that in most reasonable places

        in a PROG or CEXPR, (FRAME) will be the frame of that PROG or

        CEXPR's activation.  This means that constructions like (ACCESS

        (ACCESS (FRAME))) will have the correct meaning, no matter how

        deeply nested they are.  




                                                               VII.1.4 98





            ACCESS returns the access frame of its argument.  

            CONTROL returns the control frame of its argument.  

            EXPRESSION returns the expression whose evaluation created

        the frame supplied.  It is useful for hunting around in the frame

        structure.  

        The argument to ACCESS, CONTROL, or EXPRESSION must be a

        legitimate frame.  If it is not we get the error message:  

             BAD FRAME SUPPLIED // FRAME <- ?  

            By "legitimate frame" is meant anything that contains a

        pointer to an internal fr.  This includes all the data types of

        this section, frames, tags, and closures.  In what follows,

        "frame" is used ambiguously to refer to any of these or "*FRAMEs"

        in particular.  The ambiguity is harmless because the system

        never cares which you mean.  



        B. (TAG [atom])                                 LSUBR 
           (ACTBLOCK)                                   SUBR 

            TAG searches the control link chain from (FRAME) for the

        first activation block containing a statement label :atom.  It

        returns a tag structure whose frame is that activation block and

        whose body-pointer is to that statement label.  

            TAG of no arguments is equivalent to ACTBLOCK, which searches

        for the first activation block (frame with a body) in its

        environment and returns a tag to the beginning of the body.  If

        either a TAG or ACTBLOCK is unsuccessful in its search it returns

        NIL.  




                                                              VII.1.4 99







        C. (VFRAME atom [frame (FRAME)])                LSUBR 

            VFRAME searches up the access link chain from frame until it

        finds a frame in which atom is bound as a Conniver variable.  It

        returns that frame.  



        D. (CLOSURE procedure [frame (FRAME)])          LSUBR 

            CLOSURE produces the lambda-closure of the procedure

        (function, method) indicated.  This is an object of the form

        (*CLOSURE procedure fr).  Later invocation of the closure (see

        CALL) causes the environment of the procedure (its access

        pointer, where it searches for bindings of free variables, tags,

        etc.) to be frame E.g. If X = 4 then:  

          (CALL ((CLAMBDA (X) (CLOSURE '(CLAMBDA (Y) (+ X Y)))) 3) 5) 

        has the value 8 but 

          (CALL ((CLAMBDA (X) '(CLAMBDA (Y) (+ X Y))) 3) 5) 

        has the value 9.  

        This is the classical "FUNARG" device.  



        E. (SETACCESS frame1 frame2)                    SUBR 
           (SETCONTROL frame1 frame2)                   SUBR 

            These functions are used to alter the ALINK and CLINK,

        respectively, of frame1 to be frame2.  These will alter the

        locatives of free variables referred to in frame1, and the frame

        to which it returns.  Each function returns its second argument.






                                                              VII.1.4 100





        F. (SAMEFRAME frame1 frame2)                    SUBR 

            Functions like FRAME cons a new list structure each time they

        are called, so EQ will not work as an identity test on frames.

        (EQUAL will not work because frames may be circular.)  SAMEFRAME

        should be used.  It returns non-NIL if and only if frame1 and

        frame2 actually refer to the same fr.  










































                                                              VII.1.5 101





        VII.1.5 Alteration of Flow of Control 



        A. (CONTINUE frame)                             CINT 
           (GO atom-or-tag)                             CINT 

            These two functions cause a given fr to become the current

        process description; that is, they cause control to resume in a

        previously constructed frame.  GO is a special case of CONTINUE

        which takes only a tag argument; (GO tag) is equivalent to

        (CONTINUE tag), but GO has other uses.  GO always evaluates its

        argument, avoiding the ambiguity of Lisp.  If its argument is an

        atom, (GO arg) is equivalent to (GO (TAG arg)); that is, it

        searches up the control link chain from (FRAME) for a statement

        label ":arg."  Execution then proceeds from the statement label

        found.  If the argument is of the wrong type or an atomic tag

        cannot be found we get:  

                FOLLOWING NOT SEEN AS TAG--argument--GO // ARG <- ?  

        CONTINUE can cause the error 

                BAD FRAME SUPPLIED // FRAME <- ?  



        B. (EXIT value [frame (ACTBLOCK)])             CINT 
           (RETURN value)                               CINT 
           (DISMISS [frame first-non-COND-frame])       CINT 

            EXIT returns from the frame indicated with the value

        indicated.  If no frame is given it returns from the nearest

        activation block.  ␈↓↓Caution␈↓:  COND causes an activation block.

        RETURN returns from the nearest non-COND activation block.

        DISMISS  is EXIT from the frame specified with the value NIL.  If




                                                              VII.1.5 102





        no frame is given it does a (RETURN NIL).  If there is no

        activation block to RETURN from or EXIT from we get:  

                NO FRAME WITH BODY--EXIT // FRAME <- ?  

        or 

                NO NON-COND FRAME WITH BODY--RETURN // FRAME <- ?  

        and the frame you RETURN will be EXITed with no further checking

        for bodies.  

            If DISMISS or EXIT is given a non-frame they complain:  

                BAD FRAME SUPPLIED // FRAME <- ?  



        C. (ADIEU pos1...posN)                          CEXPR 
           (AU-REVOIR pos1...posN)                      CEXPR 

            These functions return to TRY-NEXT from a generator, NOTEing

        possibilities 1 ... N in that order (None may be supplied.  See

        NOTE).  ADIEU leaves for good but AU-REVOIR finishes by noting a

        tag inside AU-REVOIR so that TRY-NEXT can resume the generator

        where it left off.  The value of AU-REVOIR, on resumption, is the

        message passed in TRY-NEXT (see TRY-NEXT).  




















                                                              VII.1.6 103





        VII.1.6 Relative Evaluation 



        A. (CEVAL expression [frame (FRAME)])           CINT,LSUBR 

            This is the standard relative evaluation function. The

        expression is evaluated with respect to the frame specified

        (default, the current environment) as its access frame.  If the

        frame supplied is not legitimate, we get:  

             BAD FRAME SUPPLIED // FRAME <- ?  

            The LSUBR definition of CEVAL can be used to do Conniver

        evaluations from Lisp.  Unfortunately, if you use it to do

        something really clever, you probably are doing the wrong thing.

        See Chapt. VI for an account of the dangers involved.  



        B. (CALL functional-argument arg1 ... argN)     CINT 

            CALL applies the functional argument to the arguments

        supplied. It avoids the Lisp ambiguity in the case that a

        functional argument is the value of a variable and we have no way

        of guaranteeing that it has no function property.  The functional

        argument may be a function, generator, or closure of a function

        or generator.  



        C. (INVOKE method pattern)                      CINT 

            INVOKE attempts to match the pattern of the given method

        against pattern.  If the match fails, the value is NIL.

        Otherwise, the method-pattern alist generated is used to start




                                                              VII.1.6 104





        the method's variable bindings, and its body is executed as a

        PROG, its last expression yielding its value (unless the flow of

        control is altered).  
















































                                                              VII.1.7 105





        VII.1.7  Variable manipulators:  



        A. (VLOC atom [frame (FRAME)])                  LSUBR 
           (RVALUE atom [frame (FRAME)])                LSUBR 
           (/, 'atom)                                   FSUBR 
           (LVALUE 'atom)                               FSUBR 
           (ASSIGNED? atom)                             SUBR 


            VLOC returns a locative to the value of the atom supplied if

        it is found in some frame in the access link chain starting with

        the frame specified; if not, it returns NIL.  

            RVALUE returns the real value of the atom given in the frame

        specified (it does not check for *UNASSIGNED).  If either VLOC or

        RVALUE are given an illegal frame, we get:  

            BAD FRAME SUPPLIED // FRAME <- ?  

         (/, atom) (abbreviated ",atom" via macro-characters) gets the

        current Conniver value of the atom.  This is how Lisp code called

        by Conniver code gets the value of Conniver variables.  

            LVALUE gets the Lisp value of its argument.  (LVALUE atom) is

        equivalent to (but not identical to) @atom.  

            ASSIGNED returns as its value, T if its argument has a value

        (other than *UNASSIGNED) and NIL if it is unassigned.  



        B. (CSET atom value [frame (FRAME)])            LSUBR 
           (CSETQ 'atom1 value1 ... 'atomN valueN)      CINT,FSUBR 
           (UNASSIGN atom)                              SUBR 


            CSET is the most powerful assignment operator; it sets the

        atom to the value relative to the frame specified.  




                                                              VII.1.7 106





            CSETQ is a minor convenience; it does not evaluate its odd-

        numbered arguments (the atoms).  

            UNASSIGN sets its argument to *UNASSIGNED.  
















































                                                              VII.1.8 107





        VII.1.8 Possibilities lists 

            A possibilities list (created by FETCH or a generator

        function) has the following format.  



                possibilities = ((*POSSIBILITIES thing) 

                                 last-possibility 

                                 pos1 ... posN) 

                thing = expression or pattern that created this list 

                posI =  (*METHOD method methalist callalist pattern)↑ 

                        (*GENERATOR form)↑ 

                        (*AU-REVOIR fr)↑ 

                        (*ITEM item-datum alist)↑ 

                        (*NOTE alist) ↑ 

                        (user-defined-pos-type ...)↑ 

                        ␈↓↓anything␈↓←␈↓↓else␈↓ 

                 last-possibility = *IGNORE ↑ (*BLOCK *processes*)  ↑

        previous-pos1 



        Thus anything may be a possibility but the specifically mentioned

        types have special interpretation in:  

        A. (TRY-NEXT possibilities [nomore NIL] [message NIL]) 

                                                        CINT 

        TRY-NEXT is used to try the first possibility on the

        possibilities list.  In doing so, it clobbers the list, removing

        the first one.  If there are none, it evaluates nomore and




                                                              VII.1.8 108





        returns the value.  The action taken by TRY-NEXT on each type of

        possibility is as follows:  

           1.  (*METHOD method methalist callalist callpattern) 

            The method is invoked, with initial bindings given by

        methalist.  (The two alists are usually from the MATCH that FETCH

        used to filter out useless methods.)  It may generate new

        possibilities using ADIEU or AU-REVOIR.  The new possibilities

        are then spliced into the current one, replacing the method

        possibility which generated them.  TRY-NEXT then loops back to

        try the first possibility in the newly augmented possibilities

        list.  The callpattern is used by INSTANCE inside the method for

        generating output alists.  

            Method possibilities are assumed to behave as a kind of

        generator, as just described.  If they return a value (e.g., by

        running off the end of their bodies), the value is ignored.  

           2.  (*GENERATOR form) 

            Exactly the same as a method except that the form is

        evaluated rather than the method invoked.  Within a generator,

        NOTE (see below) works, but INSTANCE does not.  

           3.  (*AU-REVOIR fr) 

            This is the way AU-REVOIR can be resumed.  The TRY-NEXT goes

        off to the appropriate place in the AU-REVOIR which passed this

        back.  The AU-REVOIR returns to its caller (the generator or

        method) with the optional TRY-NEXT message as its value.  

           4.  (*ITEM item-datum alist) 




                                                              VII.1.8 109





            The alist is a list of variable-value pairs probably

        constructed by the matcher.  The variables are set to the

        indicated values and the item-datum is returned as the value of

        TRY-NEXT.   

           5. (*NOTE alist) 

            This type of possibility has the same side effect as a *ITEM

        possibility with the same alist, but returns the entire

        possibility instead of an item.  These are produced by the

        function INSTANCE mentioned below, and are a method's way of

        simulating items.  

           6. (user-defined-pos-type...) 

            If a possibility is non-atomic, and begins with an atom with

        a *POSSIBILITY property, that property is assumed to be a

        function of one argument.  TRY-NEXT calls that function with the

        possibility as argument, and returns whatever value the function

        produces.  For example, to create possibilities of the form

        (*ASSUMPTION item con), which return item and have the side

        effect of setting CONTEXT to con, define 


           (DEFUN ASPOS (POS) 
              (CSETQ CONTEXT (CADDR POS)) 
              (CADR POS)) 

           (DEFPROP *ASSUMPTION ASPOS *POSSIBILITY) 

          7.  Anything else is returned as the value of the TRY-NEXT.  










                                                              VII.1.8 110





            At any given time, the last-possibility slot of the

        possibilities list contains the last possibility that was

        returned.  Initially, this is *IGNORE; when control is in the

        process of entering a method, it is (*BLOCK *ready-processes*),

        which structure is used to avoid certain timing errors.  This

        elaborate machinery is present so that two not-necessarily-

        synchronized processes may suck possibilities out of the same

        list and be sure of getting exactly the same possibilities in

        exactly the same order.  I (DVM) am not to blame for it.  

            Thus we see that TRY-NEXT does not stop churning back for

        more possibilities created by called generators until either the

        possibilities list is empty (i.e., ((*POSSIBILITIES...) *IGNORE)

        or an item possibility or an "anything else" is first on the

        list.  If TRY-NEXT is given a bad possibilities list we get.  

                BAD POSSIBILITIES LIST 



        B. (GENERATE 'form)                             CEXPR 

            This function takes one unevaluated argument, which it

        assumes is a generator form.  It returns a possibilities list

        which starts with the first non-method or generator possibility

        returned by the form.  Thus, (TRY-NEXT (GENERATE form)) is

        equivalent to (TRY-NEXT !"((*POSSIBILITIES form) *IGNORE

        (*GENERATOR form))), but (GENERATE form) is ␈↓↓not␈↓ equivalent to

        !"((*POSSIBILITIES form) *IGNORE (*GENERATOR form))), because the

        generator is actually called by GENERATE.  




                                                              VII.1.8 111





            A method makes item possibilities as instances of its

        invocation pattern with:  

        C. (INSTANCE)                                   FSUBR 

        which returns the current instance, in the form (*NOTE alist),

        where alist is a pairing of the variables in the callalist of the

        current method with values obtained in a new match of the method

        alist.  It will get upset if there are unassigned variables in

        the pattern and will ask you to assign them.  



        A generator or method may note a new possibility via 

        D. (NOTE [possibility (INSTANCE)] pos2...posN)   LSUBR 



            This function adds each of its arguments to the current

        possibilities list; hence, it can be called only in a generator.

        It cannot be called with zero arguments; (NOTE) means (NOTE

        (INSTANCE)).   


        E. (ADIEU pos1...posN)                          CEXPR 
           (AU-REVOIR pos...posN)                       CEXPR 

            If a generator (including a method) wants to get the

        possibilities list of the TRY-NEXT it feeds, it can:  

           See Sect. VII.1.5.  



        F. (GET-POSSIBILITIES)                          FSUBR 








                                                              VII.1.8 112





        It can replace the possibilities list of that TRY-NEXT by:  

        G. (SET-POSSIBILITIES possibilities-list)       SUBR 


















































                                                              VII.1.9 113





        VII.1.9 The Interrupt System 



            In this section we outline the Conniver interrupt system in

        its crudest form.  The system uses it for errors and calling if-

        added methods.  These uses are described in the remaining

        sections of the appendix.  



        A. (CINTERRUPT expression)                      SUBR 
           (NOW expression)                             SUBR 


            These two functions both cause expression to be evaluated as

        soon as control is next in the Conniver evaluator (if interrupts

        are allowed; see B).  They may be called from Lisp, in which case

        the interruptions will be deferred until the  current Lisp

        evaluation is over.  The difference between them is that

        CINTERRUPT stacks its interrupt so that all previously ordered

        interrupts will be run first, whereas NOW causes expression to be

        evaluated before the old ones.  



        B. (ALLOW 'T-or-NIL)                             FSUBR 

            (ALLOW NIL) causes interrupts to be disabled; i.e.,

        CINTERRUPT and NOW will stack expressions that are not evaluated.

        ALLOW of anything else enables interrupts; at the next possible

        place, all pending interrupts will be run.  








                                                                VII.2 114





        VII.2 The Data Base 



            The Conniver data base is a hierarchical structure of

        ␈↓↓contexts␈↓, or a "tree" of ␈↓↓context␈↓-␈↓↓layers␈↓, containing four types of

        ␈↓↓data␈↓:  ␈↓↓objects␈↓, ␈↓↓item␈↓ ␈↓↓data␈↓, ␈↓↓methods␈↓, and ␈↓↓method␈↓ ␈↓↓closures␈↓.  

            Objects are of the form:  

                (*OBJECT arbitrary-structure *c-markers*).  

            Item data are of the form:  

                (item *c-markers*) 

        where item is any non-circular list structure.  

            Methods look like 

                (type name pattern body *c-markers*), 

        where type is IF-NEEDED, IF-ADDED, or IF-REMOVED, or a user-

        defined method type; name is an atom which is the method's name

        unless it is NIL; pattern is a non-circular list structure with

        all variables (if any) marked as !>var, !<var, !,var, etc.;  and

        body is a function body.  

            Method closures look like 

                (*CLOSURE method fr *c-markers*), 

        where method is a method, and fr is an internal frame pointer.  

            Any datum may be given an atomic name by PUTPROPing it on the

        atom under the indicator DATUM.  This is done automatically by

        the method-defining functions, but must be done manually for

        objects, item data, and closures.  The function NAME-DATUM may be

        used for this.  




                                                                VII.2 115





            All such data have (possibly NIL) lists of ␈↓↓c␈↓-␈↓↓markers␈↓

        associated with them.  A c-marker is of the form 

                (lnum (refco . status) *property-pairs*) 

        where lnum is a layer number; refco is a reference count of the

        number of references besides this one to the layer number lnum by

        this datum; status is +, NIL, or a list of lnums of layers where

        the c-marker is ␈↓↓cancelled␈↓;  and property-pairs are of the form

        (ind prop . status), where ind and prop are arbitrary, and status

        is as for the whole datum.  The c-markers on each datum are in

        order of decreasing lnums, as are the lnums in a status.  

            A c-marker or status with a given lnum indicates a ␈↓↓mention␈↓ of

        its datum by the ␈↓↓context␈↓-␈↓↓layer␈↓ associated with that lnum.  A

        context-layer is of the form 

                (*LAYER lnum *data*) 

        where lnum is its unique layer number, and data are the data it

        mentions.   

            A context is a list like 

                (*CONTEXT *layers*), 

        where layers must be in order of decreasing lnums.  It is worth

        mentioning here that none of the functions that depend on an

        explicit or implicit context argument check for the presence of

        the *CONTEXT flag at the beginning of the context.  Hence, any

        list with a list of layers as its cdr is a legal context; in

        particular, (CDR context) = (POP-CONTEXT context) for all

        practical purposes.  




                                                                VII.2 116





            Every datum has various properties, including presence or

        absence, which vary from context to context.  Typically, data-

        base changing functions (like ADD, REMOVE, DPUT, and DREM) apply

        only to the current context and its subcontexts, while data-base

        searchers (like FETCH, PRESENT, and DGET) search the present

        context from the most local layer upwards, ignoring all canceled

        c-markers or pairs.  These notions will now be made precise.

        (The next two paragraphs may be ignored.) 

            Each context rigorously defines the status of every datum as

        ␈↓↓present␈↓ or ␈↓↓absent␈↓, as follows:  if the datum has a c-marker whose

        lnum corresponds to some layer of this context and whose status

        is + or a list with no lnums corresponding to layers of the

        context, it is present, else absent.  In other words, it is

        present if it has at least one ␈↓↓uncancelled␈↓ c-marker.  In

        particular, if it is unmentioned by all layers in the context, it

        is absent.  

            Each context also determines the properties that a datum has,

        as follows:  its property for indicator ind is the prop of the

        first property pair in a c-marker with lnum corresponding to some

        layer of the context, such that that pair is uncancelled in the

        context; i.e., the first pair whose status shares no lnums with

        layers of the context.  If there is no such pair, the datum has

        no such property.  

            Every c-marker must specify either non-NIL status, or non-NIL

        property-pairs, or non-zero refco, or any combination, and cannot




                                                                VII.2 117





        be cancelled by its own lnum, or it does not constitute a

        mention.  System functions delete all c-markers of the forms (n

        (0)); a status of the form (...n...) which appears anywhere in a

        c-marker (n...) is converted to NIL.  For example, if (5 (0 . 5))

        ever arises, it is converted to (5 (0)) and deleted.  

            When a layer is not pointed to by anything, it is subject to

        garbage collection.  All c-markers embodying a mention by it will

        be deleted from their data.  

            Item data, methods, and method closures are ␈↓↓indexable␈↓ data;

        they can be referred to by pattern in FETCH and other functions.

        This ␈↓↓indexing␈↓ is done automatically by the system whenever an

        unmentioned datum becomes mentioned (by ADD, DPUT, and other

        functions); ␈↓↓unindexing␈↓ occurs when its last mention is removed

        (by REMOVE, DREM, the garbage collector, etc.).  Anonymous

        unindexed item data and methods are subject to garbage collection

        if unprotected.  






















                                                                VII.2 118





        ␈↓↓Data␈↓-␈↓↓Base␈↓ ␈↓↓Errors␈↓ 



            The data-base functions are tightly interwoven.  They all

        call a common body of invisible functions which analyze their

        arguments;   it is these that print most error messages.  Many

        functions generate the following two messages:  

                TOO FEW ARGUMENTS--function // RESULT <- ? 
                TOO MANY ARGUMENTS--function // GO ON?  

        The first will cause whatever you RETURN to be the value of the

        function.  The second will ignore the extra arguments if you

        proceed.   

            Many functions use system routines to break a datum into

        usable chunks.  They can generate the message 

                MEANINGLESS DATUM -- function // DATUM <- ?  

        If you RETURN a better datum, the system will use it in place of

        the bad one.  
























                                                              VII.2.1 119





        VII.2.1 Data-Base Initialization 



        (DATA-INIT [n 100] [m 10]) 					SUBR 

            This function wipes out all currently existing contexts, and

        unindexes all indexable data.  It creates a brand-new data base

        governed by the paramters n and m.  n is the total number of

        context layers allowed; if the data-base functions ever attempt

        to maintain more than this number at once, the message 

                TOO MANY CONTEXT LAYERS -- LAYER 

        will occur.  (See LAYER for a more complete account.) 

            The second parameter, m, is the increment between the numbers

        of context layers consecutively generated by LAYER.  Given the

        ordering constraint on layers, and the fact that SPLICE (qv.)

        must be able to generate layers with lnums between those of any

        two layers, even if they were generated consecutively, they

        cannot be numbered 0, 1, 2,..., but 0, m, 2m, 3m,....  

            Conniver does a (DATA-INIT 100. 10.) when it is loaded,

        creating a data base with at most 100 layers at a time, numbered

        0, 10., 20., 30.,....  
















                                                              VII.2.2 120





        VII.2.2 ␈↓↓Datum␈↓ ␈↓↓Creation␈↓ ␈↓↓and␈↓ ␈↓↓Manipulation␈↓ 



        A. (OBJECT [structure NIL])                     LSUBR 

        creates and returns a brand-new object of the form (*OBJECT

        structure), where structure is arbitrary.  This object is

        initially absent in all contexts, and, of course, not EQ to any

        other.   





        B. (NAME-DATUM datum atom)                     SUBR 

            This function causes datum to be called by the name atom in

        all future dealings with the system.  It returns the atom.  If

        datum is as yet unmentioned by any contexts, this is equivalent

        to (PUTPROP atom datum), but NAME-DATUM avoids certain timing

        errors associated with the other method.  

            Once a datum is named, the name should be used thenceforth in

        referring to it.  If an already-named datum is renamed, the

        system will use the new name from then on.  

            One reason for naming data is so they can be used in items.

        A pointer to an actual non-atomic datum as a component of an item

        (as, (POSSIBLE ((INNOCENT NIXON) (0 (0 . +))))) is a no-no.  





        C. (DATUM item [name])                         LSUBR 

            Item data are normally created implicitly whenever the user




                                                              VII.2.2 121





        names one with a skeleton that does not refer to any currently

        indexed item datum.  If, however, the user creates an item datum

        himself, by using LIST on an item, it is obviously guaranteed not

        to be EQ to an indexed item datum for the same item (if any).

        Thus, if he executes (REALIZE (LIST '(LINE G001))) and ((LINE

        G001) (9 (0) (ABSENT T . +))) is already indexed, the new one

        will be indexed as well.  (The indexer could check for this, but

        it would slow things down.) Then FETCH will find both, and

        PRESENT will find an unpredictable one of them.  To get around

        this problem, use DATUM instead of LIST.  DATUM returns LIST of

        the result only if it can't find it in the index; if it can, it

        returns the unique item datum with that instantiated skeleton as

        its item.  

            If DATUM is given a second argument, it becomes the name of

        the datum, and is the value returned.  



        D. (ITEM item-datum)                            SUBR 

            This function is equivalent to CAR for the usual type of item

        datum, but also works on atomic-named data.  It is the inverse of

        DATUM.  Thus, if you execute (NAME-DATUM (ADD '(GZORN ZEP)) 'FOO)

        then (ITEM 'FOO) = (GZORN ZEP), and 

                (DATUM (ITEM 'FOO)) = FOO 

                (ITEM (DATUM '(GZORN ZEP))) = (GZORN ZEP) 


        E. (IF-ADDED ['atom] 'pattern '*body*)          FSUBR 
           (IF-REMOVED ['atom] 'pattern '*body*)        FSUBR 
           (IF-NEEDED ['atom] 'pattern '*body*)         FSUBR 



                                                              VII.2.2 122






           See sect. VII.1.2.B. 



        F. (METHOD-TYPE atom)                           SUBR 
           (DELETE-METHOD-TYPE atom)                    SUBR 


            These functions are used to define new method types, in

        addition to IF-ADDED, IF-REMOVED, and IF-NEEDED.  (METHOD-TYPE

        atom) causes atom to become defined as a method-defining function

        just like IF-ADDED, with its own index.  If DATA-INIT is

        performed subsequently, all such methods will be deleted.  For

        example, following (METHOD-TYPE 'PRE-ADD), 

        (ADD 
          (PRE-ADD P1 (ON !>X !>Y) 
             (AND (PRESENT (ON !,X !>Z)) 
                  (REMOVE !"(ON ,X ,Z))))) 


        would define and add a new method of this type.  Presumably, such

        a new method is intended to be used with a user's own ADD

        function; he is responsible for setting up routines (using

        FETCHM, INVOKE, and TRY-NEXT) to use the method properly.  

            If the user wishes to define methods of the new type with

        some function of a different format from that of the standard

        kinds, he should define the defining function first; METHOD-TYPE

        will avoid redefining it.  












                                                              VII.2.3 123





        VII.2.3 Enlarging, Depleting, and Searching the Data Base 



        A. (REALIZE datum [context CONTEXT])            CEXPR,LSUBR 
           (UNREALIZE datum [context CONTEXT])          CEXPR,LSUBR 
           (ACTUALIZE datum [context CONTEXT])          LSUBR 
           (UNACTUALIZE datum [context CONTEXT])        LSUBR 
           (ADD item [context CONTEXT])                 CEXPR,LSUBR 
           (REMOVE item [context CONTEXT])              CEXPR,LSUBR 
           (INSERT item [context CONTEXT])              LSUBR 
           (KILL item [context CONTEXT])                LSUBR 


            These functions make datum present (REALIZE, ACTUALIZE, ADD,

        INSERT) or absent (UNREALIZE, UNACTUALIZE, REMOVE, KILL), by

        giving it "+" status in the c-marker for context's first layer,

        or by canceling all outstanding c-markers, respectively.  Here,

        "datum" means datum (REALIZE, UNREALIZE, ACTUALIZE, UNACTUALIZE)

        or "item datum referred to by item" (ADD, REMOVE, INSERT, KILL).

        All of them return datum.  (ADD and REMOVE can be used to alter

        the status of data not referred to by skeleton; see VII.2.3.B.) 

            The effects of these functions are invisible in all super-

        contexts of context; these effects will be collected as garbage

        if the top layer is ever caught unprotected by the garbage

        collector.   

            If ADD or REALIZE is given a item or indexable datum

        argument, respectively, all if-added methods matching datum's

        name that are present in context will be invoked.  Similarly,

        UNREALIZE and REMOVE invoke if-removed methods matching datum's

        name.  In all cases, the data base change occurs before any

        methods are called.  Methods are called only if datum's status




                                                              VII.2.3 124





        changes; i.e., realizing a present, or unrealizing an absent,

        datum is a no-op.  Methods are called by stacking invocations of

        them as Conniver interrupts.  Hence, (ALLOW NIL) will cause all

        if-addeds to remain uninvoked until interrupts are re-enabled.  

            Warning!  The LSUBR versions of these four functions execute

        hidden CEVALs to accomplish the method invocations.  If the

        methods do anything really clever and subtle, invoking them will

        probably screw your program.  



        B. (ADD atom [context CONTEXT])                 CEXPR,LSUBR 
           (REMOVE atom [context CONTEXT])              CEXPR,LSUBR 
           (INSERT atom [context CONTEXT])              LSUBR 
           (KILL atom [context CONTEXT])                LSUBR 


            If ADD and REMOVE are given atomic arguments, they are

        synonymous with REALIZE and UNREALIZE; INSERT and KILL with such

        arguments are synonymous with ACTUALIZE and UNACTUALIZE.  The

        most common use of this extra meaning is in using ADD to add

        methods, which usually have atomic names.  



        C. (FETCH pattern [context CONTEXT])            LSUBR 
           (FETCHI pattern [context CONTEXT])           LSUBR 
           (FETCHM pattern [type 'IF-NEEDED] [context CONTEXT]) 
                                                        LSUBR 


            FETCH returns a possibilities list consisting of item

        possibilities for all items present in context that match

        pattern; followed by method possibilities for all if-needed

        methods in context whose patterns match pattern.  For the format




                                                              VII.2.3 125





        of these lists, see Sect. VII.1.8.  

            FETCHI returns a possibilities list containing only the item

        possibilities.  FETCHM returns a list of only the method

        possibilities of the type type, that are present in context and

        match pattern.  (Type may be a user-defined type (Sect VII.2.2).)












































                                                              VII.2.4 126





        VII.2.4 Properties of Data 



        A. (REAL datum [context CONTEXT])               LSUBR 
           (UNREAL datum [context CONTEXT])             LSUBR 
           (PRESENT pattern [context CONTEXT])          LSUBR 
           (ABSENT item [context CONTEXT])              LSUBR 


            These functions return datum if and only if it is present

        (REAL, PRESENT) or absent (UNREAL, ABSENT) in context, and NIL

        otherwise.  REAL and UNREAL are handed their data arguments

        directly; PRESENT tries to return a randomly chosen present item

        that matches pattern; ABSENT takes DATUM (qv.) of its argument

        and then calls UNREAL.  

            PRESENT behaves a lot like (TRY-NEXT (FETCHI pattern)); in

        particular, it assigns any variables in pattern to the pieces of

        the item that they matched.  



        B. (DPUT datum property indicator [context CONTEXT]) 
                                                        LSUBR 
           (DGET datum indicator [context CONTEXT])     LSUBR 
           (DREM datum indicator [context CONTEXT])     LSUBR 

           (DPUT+ datum property indicator [context CONTEXT]) 
                                                        LSUBR 
           (DGET+ datum indicator [context CONTEXT])    LSUBR 
           (DREM+ datum indicator [context CONTEXT])    LSUBR 


            DPUT associates the pair (indicator property . +) with datum

        in the first ("most local") layer of context; like REALIZE,

        UNREALIZE, and their ilk, these effects are invisible above

        context and garbage-collectable if its top layer is reclaimed.

        DGET finds the first uncancelled pair associated with indicator




                                                              VII.2.4 127





        in any layer of context, starting with its first layer; if there

        is no such pair, its value is NIL.  DREM has the same value, but,

        as a side effect, cancels all uncancelled pairs starting with

        indicator; it does it by adding the lnum for the top layer of

        context to the status for all these pairs.  Thus, after a DREM,

        DGET for the same datum, indicator, and context will return NIL.

            DPUT+, DGET+, and DREM+ are exactly the same, but they ignore

        all context layers before the first in which datum has

        uncancelled status.  Thus, DPUT+ gives a datum properties in the

        context in which it is realized.  This is useful if a property

        happens to be calculated for the first time in a hypothetical

        context, but is itself non-hypothetical; DPUT+ makes sure it is

        visible from all subcontexts of the one in which the datum first

        appeared, and saves having to calculate it repeatedly, once per

        hypothesis.  If DPUT+ is given a datum which is absent in

        context, the error message 

                ABSENT DATUM -- DPUT+ // GO ON?  

        occurs.  



        C. (DPUTL datum property indicator layer)       SUBR 
           (DGETL datum indicator layer)                SUBR 
           (DREML datum indicator layer)                SUBR 


            These functions manipulate properties in an explicitly given

        context layer.  DPUTL associates property with indicator in the

        c-marker for layer on datum.  As usual, these effects are

        invisible in super-contexts not containing layer, and will be




                                                              VII.2.4 128





        garbage-collected if layer is.  DGETL and DREML search the c-

        marker of layer on datum for a pair with first element =

        indicator, and return it, or NIL if there isn't one; DREML

        removes what it finds.  














































                                                              VII.2.5 129





        VII.2.5 Manipulating Contexts 



        A. (LAYER)                                      LSUBR 
           (FLUSH layer)                                SUBR 


            LAYER returns a new layer with a number higher than that of

        any other.  (It uses the second argument to DATA-INIT (qv.),

        adding it to the number of the previous one it generated.)  If

        there are as many layers already as provided for by DATA-INIT,

        LAYER calls the context layer garbage collector to free space for

        more.  If all the places are taken, the message 

                TOO MANY CONTEXT LAYERS -- LAYER 

        is generated.  

            FLUSH removes all copies of the lnum of its argument from all

        data mentioned by it.  If some datum loses all of its c-markers

        because of it, it will be unindexed.  The error messages that can

        come out are due to Conniver errors, which should be ignored,

        since we do not wish to hear of unpleasant things.  They are:  

                NO REFERENCE COUNT FOR LAYER lnum 
                    ON DATUM datum --FLUSH1 
                TOO FEW REFERENCES TO LAYER lnum 
                    ON DATUM datum --FLUSH1 

        B. (PUSH-CONTEXT [context CONTEXT])             LSUBR 
           (POP-CONTEXT [context CONTEXT])              LSUBR 
           (FINALIZE [context CONTEXT])                 LSUBR 
           (NEW-CONTEXT layer-list)                     SUBR 
           (SPLICE context)                             SUBR 


            These functions create new contexts, and return them.  PUSH-

        and POP-CONTEXT return contexts with one new layer added to, or




                                                              VII.2.5 130





        the front layer removed from, context, respectively.  If POP-

        CONTEXT tries to pop the last c-layer (i.e., GLOBAL) off, it errs

        with the message 

                EMPTY CONTEXT -- POP-CONTEXT // SUPER-CONTEXT <- ?  

        and returns what you give it.  

            FINALIZE has the same value as POP-CONTEXT, with the side

        effect of making its argument an equivalent context to its

        superior.  That is, all data will have the same properties in the

        super-context that they had in the original one be equivalent.  

            NEW-CONTEXT creates a context by CONSing the flag *CONTEXT

        onto layer-list.  The layers in the list must be in order of

        decreasing lnums, or the message 

                UNORDERED CONTEXT -- NEW-CONTEXT // LAYERS <- ?  

        appears, and NEW-CONTEXT tries again with the list you give it.  

            SPLICE adds a brand-new layer to context, ␈↓↓just␈↓ ␈↓↓after␈↓ its

        first frame.  This layer will have a currently unused number

        between those of its successor and predecessor.  If all such

        numbers are in use, the error message is 

                NO NEW CNUM BETWEEN low AND high -- NEWCNUM 
        SPLICE is called for its side effect.  Its value is EQ to its

        argument, but changed, of course.  

            Since SPLICE and PUSH-CONTEXT call LAYER, they can cause its

        error.   










                                                              VII.2.5 131





        C. (IN-CONTEXT context form)                    CEXPR,SUBR 

        CEVALuates form with CONTEXT rebound to context.  Thus, for

        example, 

            (IN-CONTEXT C1 '(ADD '(FALL SKY))) 

        is equivalent to 

            (ADD '(FALL SKY) C1).  

        In general, IN-CONTEXT allows you to pretend any function takes

        an optional context argument.  The SUBR version of IN-CONTEXT

        calls the Lisp CEVAL.  



        D. (MENTIONERS datum [sign NIL] [context CONTEXT]) 
                                                        LSUBR 
           (CONTEXT+ datum [context CONTEXT])           SUBR 


             MENTIONERS returns a list, in decreasing lnum order, of all

        the layers in context that mention datum.  If sign is non-NIL, it

        ignores all cancelled c-markers.  If sign does = NIL, MENTIONERS

        returns all mentioning layers.  

            CONTEXT+ returns the closest super-context of context in

        which datum was realized; i.e., whose first layer has a c-marker

        on datum with uncancelled status.  Hence, (DPUT+ datum prop ind

        context) means the same as (DPUT datum prop ind (CONTEXT+

        context)).   



        E. (C-MARKER datum layer)                     SUBR 

            This function returns the c-marker for layer on datum, or NIL

        if layer doesn't mention datum.  If layer is subsequently




                                                              VII.2.5 132





        garbage-collected, or the c-marker degenerates to the form "(n

        (0))," the c-marker will no longer be attached to datum.  Never

        say Conniver didn't give you enough rope.  
















































                                                              VII.2.6 133





        VII.2.6 The Matcher 



            The matcher is documented in detail in Sect. IV.  Here we

        merely summarize the meaning of each of the variable prefixes

        that it knows about.  


        A.  !>var -- The basic matcher variable, which matches any
        expression which does not contain any variables (after
        substitution for "!,var's"), and binds var to it on the alist for
        its side of the match.  The only exception is that a !>var
        appearing in a FETCH-pattern need not match a variable-less
        expression in a method pattern; it will be bound to *UNASSIGNED,
        and, when the method returns, will be assigned to the piece of
        method pattern it matches, with method variable values
        substituted.  The form !>(var *restrictions*) matches any
        variable-less expression such that all the restrictions are non-
        NIL when evaluated.  

        B.  !,var -- This form does not bind a variable, but refers to
        the value associated with a previous binding; either one produced
        by !> or the Conniver binding in existence when the match is
        started.   

        C.  !,(var value) -- This binds var to value, and matches
        anything that value would match.  

        D.  !<var -- In general, !<var makes sense only in a method
        pattern.  It matches only an expression with variables in it
        after substitution of values for "!,'s", and binds var on the
        proper matcher alist to that expression.  

        E.  !?var -- This is also for method patterns only.  It matches
        any expression that either !>var or !<var would match; in the
        former case, it binds var to the variable-less expression
        matched; in the latter, to *UNASSIGNED.  Hence, inside a method,
        such a variable will be assigned only if it matched a definite
        expression.   

        F.  !;var -- This is another ambiguous expression, which only
        works in FETCH-patterns.  If var is unassigned, it behaves like
        !>var; otherwise, like !,var.  

        G.  !'var -- This expression appears to have no use except in
        method patterns.  It matches any expression, without looking at
        it, and binds var to it, even if it contains variables whose



                                                              VII.2.6 134





        values could be substituted.  It is useful for doing obscure
        syntactical decompositions on patterns.  


            All these features are explained in greater detail in Sect.

        IV.   














































                                                                VII.3 135





        VII.3 Debugging in Conniver 



            There are four classes of debugging functions in Conniver:

        listen-loop (breakpoint) functions, information printers, a trace

        package, and variable monitors.   The first three classes of

        function are discussed in the three sections below.  

            Variable monitors are not a particular bunch of functions,

        but a mechanism for using Lisp functions for performing debugging

        actions when variables are referenced or changed.  It is

        implemented as follows:  Conniver variable locatives, at the top

        and lower levels, are implemented as lists of the form (atom

        value [monitor]).  The optional monitor is a Lisp LSUBR or LEXPR,

        of two or three arguments.  It will be called whenever the

        variable is accessed or set, in the former case with two

        arguments, in the latter with three.  The two arguments for the

        case of accessing are the name of the accessing function (usually

        "/,") and the locative involved.  For setting, the three

        arguments are the setting function (e.g., CSET), the locative

        before the set, and the new value.  

            There are no special functions for placing a monitor.  It can

        be done with RPLACD in the following fashion:  

            (RPLACD (VLOC atom) (LIST monitor)) 

            Another debugging feature is the }A-interrupts, described in

        Sect. V.  Each function of }A is reprinted in this section in its

        logical category; a complete listing is found in Sect. V.




                                                              VII.3.1 136





        Remember that others may be added by the user.  




















































                                                              VII.3.1 137





        VII.3.1  Listen-Loop Builders and Manipulators 



        A. (LISTEN message)                             CEXPR 
           (EAR)                                        SUBR 
           (NEAR)                                       SUBR 
           (TOP)                                        SUBR 


            These functions create and return to listen loops whose

        bodies contain loops referred to by tags of the form EAR-n.

        These are called "ears."  LISTEN creates a new such loop, which

        is a READ-CEVAL-CPRINT loop just like the top level, printing its

        message argument, followed by EAR-n.  Whenever it is ready for

        the next input, it types "←" (left-arrow or underscore).  Within

        such a loop, the following internal Lisp variables may prove

        useful:  ** is bound to the last expression read; *, to the value

        of the last expression; and ← to the expression before last.

        These must be accessed using "@."  If you wish to flush the

        current input line, type }AF, which responds with a carriage

        return and a reprint of "←." 

            Within such a loop, the tag EAR points to a place in the body

        which prints EAR-n and restarts the loop; the variable EAR-n is

        bound to that tag.  Thus (GO 'EAR) and (GO EAR-n) have the same

        effect.  Like all other tags, ears may be used for relative

        evaluations and EXITing as well as GOing; therefore, to cause a

        LISTEN to return a value, use (EXIT value EAR-n).  $P or

        (DISMISS) causes NIL to be returned.  

            The remaining functions manipulate such listen loops.  (EAR)




                                                              VII.3.1 138





        interrupts Conniver in the next possible place, sprouting a new

        ear; }AE calls this function.  (NEAR) interrupts Conniver with

        (GO 'EAR); i.e., it causes it to return to the nearest already-

        existing ear; }AN calls it.  (TOP) flushes the current Conniver

        stack, resets the ear counter to 1, unbinds all previous ears,

        and sprouts a new EAR-1; typing }AT has the same effect.  Both

        }AE and }AN work only at places where Conniver is interruptible,

        i.e., between steps in evaluation.  They cannot be used in the

        middle of infinite printouts, Lisp evaluations, or READ's.  



        B. (UP [n 1] [action 'BT] [whichlink 'CONTROL]) CEXPR 
           (DOWN [n 1] [action 'BT])                    CEXPR 
           (J [frame original-LISTEN-access-frame] [action 'BT]) 
                                                        CEXPR 


            When a LISTEN loop is created, its access and control links

        are the same.  Evaluations are with respect to them.  The

        functions of this section enable you to move this entire loop

        around the control tree to examine and alter variables and

        control structures.  UP moves the EAR-loop n frames up either the

        control or access links, depending on whichlink.  When you

        arrive, the value of action will be printed, unless it is BT (the

        default), which causes the same data to be printed that BACKTRACE

        (Sect. VII.3.2.B) would print.  DOWN moves n frames back down the

        links followed by UP.  J jumps to a new frame, from which it is

        meaningless to go DOWN.  (J) returns you to the original place in

        the tree.  




                                                              VII.3.1 139





            All this movement occurs by clobbering the access link on the

        LISTEN frame.  The current one is stored as CURFRAME.  The

        control link is not disturbed, so (DISMISS) or $P work even if

        you have moved the frame away from where it started.  

            If you attempt to go UP off the top-level EAR or DOWN further

        than you've come up, the message 

                excess FRAMES TOO FAR 

        will appear, and no action will be taken.  



        C. (CERR '*messages*)                           FSUBR 
           (CBREAK '*messages*)                         FSUBR 
           (CERRMESS)                                   SUBR 


            The messages are printed on the same line, one after another,

        those of the form "@exp" being Lisp-evaluated, after which a Lisp

        READ-EVAL-CPRINT loop is created.  CERR first prints "**ERROR**"

        and the form Conniver was working on.  Expressions of the form

        (RETURN value) cause the CERR or CBREAK to return the given

        value; $P is equivalent to (RETURN NIL).  A listen loop like this

        is created at the next Lisp-interruptible place by }AL.  

            Most catchable data base errors cause CERR-loops to be

        created.  Within such a loop, an ERRSET will catch all Lisp

        errors, including }X.  }AF will flush the current input buffer.

        (CERRMESS) causes the messages to be reprinted.  If the priority

        interrupt system has been disabled while the data base is in an

        inconsistent state, CERR or CBREAK will turn it on for the

        duration of the loop; if it is left with RETURN, it will go off




                                                              VII.3.1 140





        again.   If }G is executed, however, the data base may be

        screwed; it might be right to DATA-INIT following such a hasty

        retreat.   
















































                                                              VII.3.2 141





        VII.3.2 Data Printers 



        A. (CPRINT exp)                                 SUBR 
           (CPRIN1 exp)                                 SUBR 


            These functions behave exactly like their Lisp counterparts

        PRINT and PRIN1, except that they are "programmable," in the

        sense that special data types are printed in different ways

        according to their CAR's.  CPRIN1 prints atomic argument on the

        current line; if its argument's CAR is an atom with a CPRINT

        property, it does something special; otherwise, it CPRIN1's its

        CAR, then its CDR.  (We are not being very precise.)  If the

        argument does have a marked CAR, the CPRINT property is assumed

        to be a FEXPR or FSUBR; CPRIN1 merely applies it to the thing to

        be CPRINTed.  In all cases, CPRIN1 returns the thing printed.

        CPRINT prints a carriage return, CPRIN1's its argument, and

        prints a space, then returns its argument.  

            The built-in data types treated specially by CPRIN1 are as

        follows:  all quoted expressions, statements labels, matcher

        variables, and other system macro'd data are printed in the same

        format as they are input; tags, frames, and closures are printed

        in such a way that internal fr pointers are replaced by the

        expressions that gave rise to those fr's.  The user is free to

        add new data formats to this list by creating FEXPR's attached to

        atoms used to flag them.  For example, to make all contexts print

        out as lists of numbers, execute 





                                                              VII.3.2 142





        (DEFUN CP-CONTEXT FEXPR (CON) 
           (PRIN1 (PATH CON))   ) 

        (DEFPROP *CONTEXT CP-CONTEXT CPRINT) 


        B. (EXPRESSION frame)                           SUBR 
           (BACKTRACE [number 696969])                  LEXPR 


            EXPRESSION returns the form whose evaluation gave rise to

        frame; it is documented in Sect. VII.1.4.  

            BACKTRACE types out, in a very readable form, the expressions

        corresponding to each frame of the current process, starting with

        the current frame, and proceeding by control links to the top

        level.  The optional numerical argument may be supplied to limit

        the typeout to that many frames.  The backtrace is programmable

        in the following sense:  if an expression's CAR has a BACKTRACE

        property, the property is assumed to be an EXPR or SUBR of two

        arguments, a frame and a list of arguments; BACKTRACE applies the

        function to the frame and CDR of the expression and does nothing

        else.  This is used internally to print EAR-frames, PROG's,

        COND's, and other things in special formats.  Another way to use

        expressions is with }AX, which causes the current (EXPRESSION

        (FRAME)) to be printed.  Execution then continues.  



        C. (PATH [context CONTEXT])                     LSUBR 

            The value of PATH is a list whose first element is *CONTEXT,

        followed by the lnums of context's layers.  Such an object serves

        no useful purpose, but it is much more more lucidly printable




                                                              VII.3.3 143





        than context itself, in general.  




















































                                                              VII.3.3 144





        VII.3.3 Tracing in Conniver 



            For those who like to trace, there is a crude trace package

        which exists as CNVR;CTRACE >.  It is not normally part of the

        Conniver system.  Suggestions and volunteers for improving it are

        welcome.  

            The Lisp tracer may also be used while Conniving.  



        A. (CTRACE '*specs*)                            CEXPR 

           Each spec is of the form (atom EN *things-to-do-on-entrance*

        EX *things-to-do-on-exit*).  The order of EX and EN is

        unimportant.   If an EN is present, a message will be printed

        when the function is entered, and the things to do will be

        EVALuated; EX is similar.  Both are optional, although leaving

        both out is a slow no-op.  If a spec is an atom, it is equivalent

        to (atom EN (CDISPLAY *ARGS) EX (CDISPLAY *VAL)).  (See below.)

        Within a traced function, *ARGS will be bound to a list of

        evaluated or unevaluated arguments (the status of each of which

        depends imaginatively on the variable declarations of the

        function); and *VAL will, on output, be bound to the value the

        function is to return.  

            Functions, generators, and methods may be traced.  










                                                              VII.3.3 145





        B. (CUNTRACE '*atoms*)                          CEXPR 

            Each atom must be a CTRACEd function, which is untraced.  





        C. (CDISPLAY *things*)                           FEXPR 

            This is a handy function in tracing.  It prints out a table

        of the Lisp value of each thing, unless it is an atom, when its

        Conniver value will be printed.  Thus, to see the arguments to a

        function and the CAR of the Conniver variable FOO, use 

        (CDISPLAY *ARGS (CAR ,FOO)), and get 

        *ARGS = whatever-they-are 

        (CAR ,FOO) = whatever-it-is.  





        D. (/:  'atom)                                    FEXPR 

             (/:  atom) is the internal representation of :atom.  The

        Lisp trace package can be used to trace the function "/:," thus

        showing your flow of control.  


















                                                              VII.3.3 146





                                  ␈↓↓Bibliography␈↓


        Bobrow, D.G. and Wegbreit, B. (1972) ␈↓↓A␈↓ ␈↓↓Model␈↓ ␈↓↓and␈↓ ␈↓↓Stack␈↓
            ␈↓↓Implementation␈↓ ␈↓↓of␈↓ ␈↓↓Multiple␈↓ ␈↓↓Environments␈↓, Bolt, Beranek, and
            Newman Inc. Report 2334.  

        Hewitt, C. (1971) ␈↓↓Description␈↓ ␈↓↓and␈↓ ␈↓↓Theoretical␈↓ ␈↓↓Analysis␈↓ (␈↓↓Using␈↓
            ␈↓↓Schemata␈↓) ␈↓↓of␈↓ ␈↓↓PLANNER␈↓:  ␈↓↓A␈↓ ␈↓↓Language␈↓ ␈↓↓for␈↓ ␈↓↓Proving␈↓ ␈↓↓Theorems␈↓ ␈↓↓and␈↓
            ␈↓↓Manipulating␈↓ ␈↓↓Models␈↓ ␈↓↓in␈↓ ␈↓↓a␈↓ ␈↓↓Robot␈↓, M.I.T. A.I. Laboratory
            Technical Report 250.  

        McCarthy, J., Abrahams, P.W., Edwards, D.J., Hart, T.P. and
            Levin, M.I. (1962) ␈↓↓LISP␈↓ ␈↓↓1.5␈↓ ␈↓↓Programmer's␈↓ ␈↓↓Manual␈↓, Cambridge,
            Mass.: The MIT Press.  

        PDP-6 (1967) ␈↓↓PDP-6␈↓ ␈↓↓LISP␈↓, M.I.T. A.I. Laboratory Memo. No. 116A.  

        Weissman, C. (1967) ␈↓↓LISP␈↓ ␈↓↓1.5␈↓ ␈↓↓Primer␈↓, Belmont, Calif.:  Dickenson
            Publishing Company, Inc.  

        White, J.L. (1970) ␈↓↓An␈↓ ␈↓↓Interim␈↓ ␈↓↓LISP␈↓ ␈↓↓User's␈↓ ␈↓↓Guide␈↓, M.I.T. A.I.
            Laboratory Memo No. 190.  




























βort